您现在的位置是:首页 >技术杂谈 >嵌入式人工智能应用- EAIDK610-part two网站首页技术杂谈
嵌入式人工智能应用- EAIDK610-part two
承接上一部分的知识点内容,我们接着介绍所需要的相关的知识。
4 makefile文件制作
在前面介绍中可以看到,如果工程只有1个或者2个源文件情况下,第3部分讲解编译操作步骤还能进行。实际情况是大部分应用需要用的源文件、头文件比较多。采用前面的编译方法是很困难,需要手动对每个文件进行编译、链接和执行。其次对文件进行管理,也是比较混乱的。在Linux开发中,我们经常用make这个工具对编译的源文件和头文件进行管理。当然也有公司会用到qmake和cmake之类更加高级的开发工具。在这里,笔者只谈到make工具的使用。
4.1 单个文件makefile制作
- 假设project 文件下面分别 main.cpp sum.hpp sum.cpp 共两个源文件和一个头文件。
- 新建一个makefile 文件
[openailab@localhost project]$ ls
main main.cpp sum.cpp sum.hpp
[openailab@localhost project]$ touch makefile
[openailab@localhost project]$ ls
main main.cpp makefile sum.cpp sum.hpp
- 编写makefile文件。首先定义编译器、头文件路径,查找所有源文件;然后把sum.cpp 和 main.cpp 分别编译成为sum.o 和 main.o 文件;最终输出可执行文件main。
CXX=g++
INCFLAGS= -I ~/Desktop/project
SRC=$(shell ls *.cpp)
OBJS=$(patsubst %.cpp,%.o,$(SRC))
all: $(OBJS)
$(CXX) $(INCFLAGS) -g -o main $^
%.o: %.cpp
$(CXX) $(INCFLAGS) -c -o $@ $<
.PHONY: clean
clean:
rm -rf *.o
- 输入make,执行编译
[openailab@localhost project]$ make
g++ -I ~/Desktop/project -c -o main.o main.cpp
g++ -I ~/Desktop/project -c -o sum.o sum.cpp
g++ -I ~/Desktop/project -g -o main main.o sum.o
- 运行可执行文件,输出结果
[openailab@localhost project]$ ./main
result is 3
hello,my world
4.2 多文件makefile制作
程序开发的时候,文件是比较多的。文件分布在不同的文件夹种。这个时候需要对多文件进行编程。
- 假设在project 文件夹中有四个文件夹:一个main文件夹,一个src文件夹,一个inc文件夹和一个object文件夹。在每个文件新建一个makefile文件,具体内容如下:
[openailab@localhost project]$ tree
.
├── inc
│ ├── add.hpp
│ ├── face.hpp
│ └── mul.hpp
├── main
│ ├── main.cpp
│ └── makefile
├── makefile
├── object
│ └── makefile
└── src
├── add.cpp
├── face.cpp
├── makefile
└── mul.cpp
inc 里面存放是头文件、main存放是main文件,src存放是库的源文件,object 现在是空,后面存放目标文件和最终生成的可执行文件。下面对每个makefile文件进行编写。
- project 的makffile文件。 编程思路:获取所有文件夹地址,变为宏定义。然后进去每个文件中,依次执行子文件中makefile。
#找到所有文件路径
PRO_PATH=$(shell pwd)
SRC_PATH=$(PRO_PATH)/src
OBJ_PATH=$(PRO_PATH)/object
MAIN_PATH=$(PRO_PATH)/main
INC_PATH=$(PRO_PATH)/inc
#注册环境变量
export PRO_PATH SRC_PATH OBJ_PATH MAIN_PATH INC_PATH
all:
make -C $(SRC_PATH)
make -C $(MAIN_PATH)
make -C $(OBJ_PATH)
.PHONY: clean
clean:
rm -rf $(OBJ_PATH)/*.o
- src里面和main的makefile文件。编程思路找到所有源文件,编译成为目标文件。然后把编译好的目标文件存放到object文件夹中。
CXX=g++
INCFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.cpp)
OBJS=$(patsubst %.cpp,%.o,$(SRC))
all: $(OBJS)
%.o: %.cpp
$(CXX) $(INCFLAGS) -c -o $(OBJ_PATH)/$@ $<
- object里面的makefile文件。编程思路:找到所有目标文件,编译成为执行文件。
CXX=g++
INCFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.o)
OBJS=$(SRC)
all: $(OBJS)
$(CXX) $(INCFLAGS) -g -o $(OBJ_PATH)/main $^
- 在project文件夹下面,执行make指令。
[openailab@localhost project]$ make
make -C /home/openailab/Desktop/project/src
make[1]: Entering directory '/home/openailab/Desktop/project/src'
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/add.o add.cpp `
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/face.o face.cpp
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/mul.o mul.cpp
make[1]: Leaving directory '/home/openailab/Desktop/project/src'
make -C /home/openailab/Desktop/project/main
make[1]: Entering directory '/home/openailab/Desktop/project/main'
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/main.o main.cpp
make[1]: Leaving directory '/home/openailab/Desktop/project/main'
make -C /home/openailab/Desktop/project/object
make[1]: Entering directory '/home/openailab/Desktop/project/object'
g++ -I /home/openailab/Desktop/project/inc -g -o /home/openailab/Desktop/project/object/main add.o face.o main.o mul.o `
make[1]: Leaving directory '/home/openailab/Desktop/project/object'
- 执行可执行文件
[openailab@localhost project]$ ./object/main
4.3 基本文件操作
文件的输入和输出操作,是一个linux一个基本要求。在这里顺带介绍以下文件输入和输出。
- 包含的头文件
#include "file.hpp"
#include "iostream"
#include "fstream"
#include "string"
- 保存文件的代码如下。第一个保存文件名,第二个是保存内容。通过输出流来进行操作。
void Save_File(std::string &file_name, std::string &file_content)
{
std::ofstream out_file;
out_file.open(file_name,std::ios::app);
if(out_file.is_open()==true)
{
out_file<<file_content<<std::endl;
out_file.close();
}
else
{
std::cout<<"open fail"<<std::endl;
}
}
- 读入的函数。第一个要读入的文件名,第二个是文件的内容。
void Read_File(std::string &file_name, std::string &file_content)
{
std::ifstream in_file;
in_file.open(file_name);
if(in_file.is_open()==true)
{
std::getline(in_file,file_content);
while(file_content.empty()==false)
{
std::cout<<file_content<<std::endl;
std::getline(in_file,file_content);
}
in_file.close();
}
else
{
std::cout<<"read fail"<<std::endl;
}
}
5 动态库和静态库制作,pkg-config 使用
库是写好的,现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
5.1 静态库制作
静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
譬如把上文提到文件的库编程静态库的开发流程作为应用。
- 前期准备 -在project的下面新建一个lib文件夹,
[openailab@localhost project]$ mkdir lib
[openailab@localhost project]$ ls
inc lib main makefile object src
[openailab@localhost project]$ cd lib
[openailab@localhost lib]$ ls
file.cpp
- 把file.cpp 编译成为file.o,对应头文件存在inc里面
[openailab@localhost lib]$ g++ -c -o file.o file.cpp -I ~/Desktop/project/inc
[openailab@localhost lib]$ ls
file.cpp file.o
- 通过ar 指令libfile.a 文件。其中lib是前缀, 静态文件后缀是.a.
[openailab@localhost lib]$ ar -rvc libfile.a file.o
r - file.o
[openailab@localhost lib]$ ls
file.cpp file.o libfile.a
- 编辑object下面的makefile文件,指定库地址和名字。
CXX=g++
INCFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.o)
OBJS=$(SRC)
LDDFLAGS= -L ~/Desktop/project/lib -lfile
all: $(OBJS)
$(CXX) $(INCFLAGS) -g -o $(OBJ_PATH)/main $^ $(LDDFLAGS)
5.2 动态库制作
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
- 把file.cpp 编译成为libfile.so,对应头文件存在inc里面
[openailab@localhost lib]$ g++ -fPIC -shared -o libfile.so file.cpp -I ~/Desktop/project/inc
[openailab@localhost lib]$ ls
file.cpp file.o libfile.a libfile.so
- object makfile文件跟静态库makefile文件是一样的。需要指定库的地址和库的名字。
CXX=g++
INCFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.o)
OBJS=$(SRC)
LDDFLAGS= -L ~/Desktop/project/lib -lfile
all: $(OBJS)
$(CXX) $(INCFLAGS) -g -o $(OBJ_PATH)/main $^ $(LDDFLAGS)
- 为了不影响系统的库文件,没有把库文件复制到系统lib文件下面。所以运行的时候需要加载动态库地址。
export LD_LIBRARY_PATH=~/Desktop/project/lib:$LD_LIBRARY_PATH
5.3 pkg-config使用
由于很多开发的工具库的文件是很多,如果在编译过程中,需要指定库文件名的话,这个开发工作量很大的。经常会用到pkg-config的这个工具帮助我们搜索需要的库和头文件的地址。
这里我们还是以libfile.so这个文件为例。
- 新建一个file.pc 文件
prefix=/home/openailab/Desktop/project
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/inc
Name: File
Description: file operation
Version: 1.0.0
Libs: -L${exec_prefix}/lib -lfile
Cflags: -I${includedir}
- 更新pkg-config的路径
[openailab@localhost project]$ export PKG_CONFIG_PATH=/home/openailab/Desktop/project/lib
[openailab@localhost project]$ echo $PKG_CONFIG_PATH
/home/openailab/Desktop/project/lib
- 更新object里面makefie文件,调用pkg-config取搜索file。
CXX=g++
CXXFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.o)
OBJS=$(SRC)
LDDFLAGS= `pkg-config --libs file`
INCFLAGS= `pkg-config --cflags file`
all: $(OBJS)
$(CXX) $(CXXFLAGS) -g -o $(OBJ_PATH)/main $^ $(LDDFLAGS) $(INCFLAGS)
- 执行make指令就好了
[openailab@localhost project]$ make
make -C /home/openailab/Desktop/project/src
make[1]: Entering directory '/home/openailab/Desktop/project/src'
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/add.o add.cpp
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/mul.o mul.cpp
make[1]: Leaving directory '/home/openailab/Desktop/project/src'
make -C /home/openailab/Desktop/project/main
make[1]: Entering directory '/home/openailab/Desktop/project/main'
g++ -I /home/openailab/Desktop/project/inc -c -o /home/openailab/Desktop/project/object/main.o main.cpp
make[1]: Leaving directory '/home/openailab/Desktop/project/main'
make -C /home/openailab/Desktop/project/object
make[1]: Entering directory '/home/openailab/Desktop/project/object'
g++ -I /home/openailab/Desktop/project/inc -g -o /home/openailab/Desktop/project/object/main add.o main.o mul.o `pkg-config --libs file` `pkg-config --cflags file`
make[1]: Leaving directory '/home/openailab/Desktop/project/object'
6 opencv 常见库的使用
前期准备工作,由于开发板内置的图形库是fastcv,在开发之前需要添加fastcv的库文件。
- 在project 下面的makefile,增加pkg-config的地址
# 找到所有文件路径
PRO_PATH=$(shell pwd)
SRC_PATH=$(PRO_PATH)/src
OBJ_PATH=$(PRO_PATH)/object
MAIN_PATH=$(PRO_PATH)/main
INC_PATH=$(PRO_PATH)/inc
LIB_PATH=$(PRO_PATH)/lib
RM= rm -rf
#注册环境变量
export PRO_PATH SRC_PATH OBJ_PATH MAIN_PATH INC_PATH LIB_PATH
export LD_LIBRARY_PATH=$(LIB_PATH):$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/usr/local/AID/pkgconfig:$PKG_CONFIG_PATH
all:
make -C $(SRC_PATH)
make -C $(MAIN_PATH)
make -C $(OBJ_PATH)
.PHONY: clean
clean:
$(RM) $(OBJ_PATH)/*.o
- 更新src和main的makefile文件,添加fastcv库
CXX=g++
SRC=$(shell ls *.cpp)
OBJS=$(patsubst %.cpp,%.o,$(SRC))
INCFLAGS= `pkg-config --cflags fastcv` -I $(INC_PATH)
LDDFLAGS= `pkg-config --libs fastcv`
all: $(OBJS)
%.o: %.cpp
$(CXX) -c -o $(OBJ_PATH)/$@ $< $(INCFLAGS) $(LDDFLAGS)
- 更新object makefile文件,添加fastcv库
CXX=g++
SRC=$(shell ls *.o)
OBJS=$(SRC)
INCFLAGS= `pkg-config --cflags fastcv` -I $(INC_PATH)
LDDFLAGS= `pkg-config --libs fastcv` -L $(LIB_PATH) -lfile
all: $(OBJS)
$(CXX) -g -o $(OBJ_PATH)/main $^ $(INCFLAGS) $(LDDFLAGS)
6.1 图形的操作基本概念
- 创建灰度图像,保存为img.jpg
cv::Mat img;
img.create(cv::Size(600,800),CV_8U);
unsigned char *ptr = img.ptr<unsigned char>(0,0);
for(int height=0;height<img.rows;height++)
{
for(int width=0;width<img.cols;width++)
{
*ptr++=255;
}
}
std::cout<<"img height is "<<img.rows<<std::endl;
std::cout<<"img width is "<<img.cols<<std::endl;
std::cout<<"img size is " <<img.size()<<std::endl;
std::cout<<"img channel is "<<img.channels()<<std::endl;
cv::imwrite("img.jpg",img);
- 创建彩色图像,保存为img.jpg
cv::Mat img;
img.create(cv::Size(600,800),CV_8UC3);
unsigned char *ptr = img.ptr<unsigned char>(0,0);
for(int height=0;height<img.rows;height++)
{
for(int width=0;width<img.cols;width++)
{
*ptr++=255;
*ptr++=0;
*ptr++=0;
}
}
std::cout<<"img height is "<<img.rows<<std::endl;
std::cout<<"img width is "<<img.cols<<std::endl;
std::cout<<"img size is " <<img.size()<<std::endl;
std::cout<<"img channel is "<<img.channels()<<std::endl;
cv::imwrite("img.jpg",img);
- 彩色图像转化成为灰度图像
cv::Mat gray_img;
cv::cvtColor(img,gray_img, cv::COLOR_BGR2GRAY);
cv::imwrite("gay_img.jpg",gray_img);
- 灰度图像转化成为彩色图像
cv::Mat clr_img;
cv::cvtColor(gray_img,clr_img, cv::COLOR_GRAY2BGR);
cv::imwrite("clr_img.jpg",clr_img);
- 缩小图像尺寸
cv::Mat resize_img;
cv::resize(img,resize_img,cv::Size(100,100));
std::cout<<"resize_img height is "<<resize_img.rows<<std::endl;
std::cout<<"resize_img width is "<<resize_img.cols<<std::endl;
std::cout<<"resize_img size is " <<resize_img.size()<<std::endl;
std::cout<<"resize_img channel is "<<resize_img.channels()<<std::endl;
cv::imwrite("resize_img.jpg",resize_img);
6.2 图形的基本绘图函数
- 画直线
cv::Point start_point,end_point;
start_point.x=100; start_point.y=100;
end_point.x=200; end_point.y=200;
cv::line(img,start_point,end_point,cv::Scalar(0,255,0),2,cv::LINE_4);
cv::imwrite("img.jpg",img);
- 画三角形
cv::Point Point[3];
Point[0].x=100; Point[0].y=100;
Point[1].x=300; Point[1].y=100;
Point[2].x=200; Point[2].y=200;
cv::line(img, Point[0],Point[1],cv::Scalar(0,255,0),2,cv::LINE_4);
cv::line(img, Point[1],Point[2],cv::Scalar(0,255,0),2,cv::LINE_4);
cv::line(img, Point[0],Point[2],cv::Scalar(0,255,0),2,cv::LINE_4);
cv::imwrite("img.jpg",img);
- 画长方形
- 方法一:指定对角线两点坐标
cv::rectangle(img,Point[0],Point[2],cv::Scalar(0,255,0),2,cv::LINE_4 );
cv::imwrite("re_img.jpg",img);
- 方法二:指定起点坐标和高度和宽度
cv::rectangle(img,cv::Rect(100,100,200,200),cv::Scalar(0,255,0),2,cv::LINE_4);
cv::imwrite("re_img1.jpg",img);
- 画圆- 指定圆点坐标和半径
cv::circle(img,Point[0],50,cv::Scalar(0,255,255),2,cv::LINE_4);
cv::imwrite("re_img3.jpg",img);
- 画椭圆-指定圆点坐标、长轴和短轴长度
cv::RotatedRect point;
point.center=cv::Point(100,100);
point.size=cv::Size(20,50);
cv::ellipse(img,point,cv::Scalar(0,255,255),2,cv::LINE_4);
cv::imwrite("re_img4.jpg",img);
- 输出文字-指定内容,启动位置,字体,缩放比例等。
cv::putText(img,"dog",cv::Point(200,200),cv::FONT_HERSHEY_COMPLEX,0.5,cv::Scalar(100,100,50),2,cv::LINE_8);
cv::imwrite("re_img6.jpg",img);
人工智能识别的时候,经常识别的地方标注文字。文字还有框来锁定,需要配合getTextSize这个函数结合使用。先获取输出字符串的大小。
int baseline;
cv::Size text_size= cv::getTextSize("this is a dog",cv::FONT_HERSHEY_SIMPLEX,2,1,&baseline);
cv::Point text_point=cv::Point(100,100);
cv::putText(img,"this is a dog",text_point,cv::FONT_HERSHEY_SIMPLEX,2,cv::Scalar(155,233,50),1,cv::LINE_4);
cv::Rect rec_point;
rec_point.x=text_point.x;
rec_point.y=text_point.y-text_size.height;
rec_point.width=text_size.width;
rec_point.height=text_size.height+baseline;
cv::rectangle(img,rec_point,cv::Scalar(0,0,255),1,cv::LINE_4);
cv::imwrite("img.jpg",img);