您现在的位置是:首页 >技术杂谈 >嵌入式人工智能应用- EAIDK610-part two网站首页技术杂谈

嵌入式人工智能应用- EAIDK610-part two

HHONGQI123 2023-05-13 00:00:03
简介不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。inc 里面存放是头文件、main存放是main文件,src存放是库的源文件,object 现在是空,后面存放目标文件和最终生成的可执行文件。由于很多开发的工具库的文件是很多,如果在编译过程中,需要指定库文件名的话,这个开发工作量很大的。库是写好的,现有的,成熟的,可以复用的代码。静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。程序开发的时候,文件是比较多的。

承接上一部分的知识点内容,我们接着介绍所需要的相关的知识。

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);

第7章介绍人工应用的部署,详见第三部分

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。