目录
一、用gcc生成静态库和动态库文件的有关操作
二、有关gcc的编译及EFF文件
三、学习opencv图像库编程
四、总结
一、用gcc生成静态库和动态库文件的有关操作
静态库和动态库简述
-
静态库
在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库 -
动态库
在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入,因此程序运行时还需要动态库存在。
1、gcc生成静态库和动态库
(1)创建目录
mkdir test1
cd test1
(2)编辑程序代码 -
hello.h
1. #infdef HELLO_H
2. #define HELLO_H
3. void hello(const char *name);
4. #endif//HELLO_H
- hello.c
#include<stdio.h>
void hello(const char *name)
{
printf("Hello%s!\n",name);
}
- main.c
#include"stdio.h"
int main()
{
hello("everyone");
return 0;
}
(3)gcc编译.o文件
gcc -c hello.c
(4)用.o文件创建静态库
创建工具:ar
命名规则:以lib为前缀,扩展名为.a
ar -crv libmyhello.a hello.o
(5)使用静态库
- 方法一
gcc -o hello main.c -L. -lmyhello
- 方法二
gcc main.c libmyhello.a -o hello
- 方法三
gcc -c main.c
gcc -o hello main.o libmyhello.a
(6)由.o文件创建动态库文件
命名规范与静态库类似,前缀为lib,只是文件扩展名为.so
gcc -shared -fPIC -o libmyhello.so hello.o
(7)在程序中使用动态库
gcc -o hello main.c -L. -lmyhello
这里显示出错,原因是找不到文件libmyhello.so,解决如下:
mv libmyhello.so/usr/lib
键入./hello,可以看到程序成功运行
(8)测试静态库和动态库同名时gcc命令使用哪个文件
- 首先先删除.c和.h外的所有文件,恢复编辑举例程序状态
- 创建静态库文件和动态库文件
- 运行gcc命令使用函数库myhello生成目标文件hello,并运行程序hello
可以发现,当静态库和动态库同名时,gcc命令会优先使用动态库,默认去连/usr/lib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可
2、改编作业一,将x2x和x2y目标文件用ar工具生成.a静态库文件,并进行链接
(1)编辑三个子程序sub1.c,sub2.c,main1.c
- x2x函数程序sub1.c
#include<stdio.h>
float x2x(int a,int b)
{
return a*b;}
- x2y函数程序sub2.c
#include<stdio.h>
float x2y(int a,int b)
{
return a+b;}
- main函数程序main.c
#include<stdio.h>
int main()
{
float x2x(int a,int b);
float x2y(int a,int b);
int x1,x2;
x1=4;
x2=5;
printf("%f %f\n",x2x(x1,x2),x2y(x1,x2));
return 0;}
(2)用gcc将三个.c文件编译为.o目标文件
gcc -c sub1.c
gcc -c sub2.c
gcc -c main1.c
(3)将x2x,x2y目标文件生成1个.a静态库文件
(4)用gcc将main函数目标文件与静态库文件链接
2、将x2x,x2y文件生成.so动态库文件
(1)生成目标文件
(2)生成共享库.so文件
(3)创建可执行程序
找不到对应.so文件,需将对应.so文件拷贝到对应路径
再次执行./main1后,程序成功运行
二、有关gcc的编译及EFF文件
1、gcc的编译
(1)示例程序test.c
#include<stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;}
(2)一步编译指令
gcc test.c -o test
(3)实质上编译的四阶段
- 预处理
gcc -E test.c -o test.i(或gcc -E test.c)
预处理代码
- 编译为汇编代码
gcc -S test.i -o test.s
汇编代码
- 汇编
gcc -c test.s -o test.o
- 连接
gcc test.o -o test
动态链接可执行文件大小
静态链接可执行文件大小
(3)执行程序
2、ELF文件的分析
(1)ELF文件的段
ELF文件格式如下图所示,位于ELF Header和Section Header Table 之间的都是段(section)。一个典型的ELF文件包含下面几个段:
text:已编译程序的指令代码
rodata:ro代表read only,即只读数据。
data:已初始化的C程序全局变量和静态局部变量,
bss:未初始化的C程序全局变量和静态局部变量。
debug:调试符号表,调试器用此段的信息帮助调试。
用readelf -S查看section的信息如下:
readelf -S
2、反汇编ELF
由于ELF文件无法被当作普通文本文件打开,如果希望直接查看一个ELF文件包含的指令和数据,需要使用反汇编的方法。
- objdump -D的反汇编
objdump -D test
- objdump -S反汇编并将其c语言源代码混合显示
gcc -o test -g test.c
objdump -S test
三、学习opencv图像库编程
安装链接opencv安装
1、图片转换
- 创建code文件夹,进入code文件夹(这里我用touch code创建文件夹后进入文件夹显示code不是目录,我将其改成了mkdir code)
mkdir code
cd code
- 创建test1.cpp文件
gedit test1.cpp
粘贴代码
#include <opencv2/highgui.hpp>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
CvPoint center;
double scale = -3;
IplImage* image = cvLoadImage("lena.jpg");
argc == 2? cvLoadImage(argv[1]) : 0;
cvShowImage("Image", image);
if (!image) return -1; center = cvPoint(image->width / 2, image->height / 2);
for (int i = 0;i<image->height;i++)
for (int j = 0;j<image->width;j++) {
double dx = (double)(j - center.x) / center.x;
double dy = (double)(i - center.y) / center.y;
double weight = exp((dx*dx + dy*dy)*scale);
uchar* ptr = &CV_IMAGE_ELEM(image, uchar, i, j * 3);
ptr[0] = cvRound(ptr[0] * weight);
ptr[1] = cvRound(ptr[1] * weight);
ptr[2] = cvRound(ptr[2] * weight);
}
Mat src;Mat dst;
src = cvarrToMat(image);
cv::imwrite("test.png", src);
cvNamedWindow("test",1); imshow("test", src);
cvWaitKey();
return 0;
}
保存后退出,并执行下面命令
g++ test1.cpp -o test1
pkg-config --cflags --libs opencv
- 在code文件夹下准备jpg图片,这里命名为lena.jpg
执行命令
./test1
由结果可看到lena.jpg变成test.png
2、视频
(1)播放视频
- 虚拟机获取摄像头权限
快捷键win+R。输入services.msc,点击确定
找到VMmare USB Aribitration S…服务,确保其显示正在…启动
点击“虚拟机”,点击“设置”
选择"USB控制器”,“USB兼容性”设置为“USB3.1”,点击确定
原博主设置的是USB3.0,我这里只显示有3.1,所以选择了3.1
再点击“虚拟机”,选择“可移动设备”的IMC Networks USB2.0 VGA UVC WebCam选项,选择连接,弹出的窗口点击“确定”
连接成功显示,见界面右下角
- 创建test2.cpp文件
gedit test2.cpp
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
//从摄像头读取视频
VideoCapture capture("scene.mp4");
//循环显示每一帧
while(1){
Mat frame;//定义一个Mat变量,用于存储每一帧的图像
capture >> frame;//读取当前帧
if(frame.empty())//播放完毕,退出
break;
imshow("读取视频帧",frame);//显示当前帧
waitKey(30);//掩饰30ms
}
system("pause");
return 0;
}
准备一个视频scene.mp4
- 编译test2.cpp文件
g++ test2.cpp -o test2
pkg-config --cflags --libs opencv
输出结果
./test2
(2) 录制视频
- 创建一个test3.cpp文件
gedit test3.cpp
/*********************************************************************
打开电脑摄像头,空格控制视频录制,ESC退出并保存视频RecordVideo.avi
*********************************************************************/
#include<iostream>
#include <opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
//打开电脑摄像头
VideoCapture cap(0);
if (!cap.isOpened())
{
cout << "error" << endl;
waitKey(0);
return 0;
}
//获得cap的分辨率
int w = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(cap.get(CV_CAP_PROP_FRAME_HEIGHT));
Size videoSize(w, h);
VideoWriter writer("RecordVideo.avi", CV_FOURCC('M', 'J', 'P', 'G'), 25, videoSize);
Mat frame;
int key;//记录键盘按键
char startOrStop = 1;//0 开始录制视频; 1 结束录制视频
char flag = 0;//正在录制标志 0-不在录制; 1-正在录制
while (1)
{
cap >> frame;
key = waitKey(100);
if (key == 32)//按下空格开始录制、暂停录制 可以来回切换
{
startOrStop = 1 - startOrStop;
if (startOrStop == 0)
{
flag = 1;
}
}
if (key == 27)//按下ESC退出整个程序,保存视频文件到磁盘
{
break;
}
if (startOrStop == 0 && flag==1)
{
writer << frame;
cout << "recording" << endl;
}
else if (startOrStop == 1)
{
flag = 0;
cout << "end recording" << endl;
}
imshow("picture", frame);
}
cap.release();
writer.release();
destroyAllWindows();
return 0;
}
保存后退出
- 编译test3.cpp文件
g++ test3.cpp -o test3
pkg-config --cflags --libs opencv
输出结果
./test3
可以看到,生成了一个Record Video.avi文件,视频开始录制,不断生成帧。
3、
(1)如果要求打开硬盘上一个视频文件来播放,请问示例代码1第7行代码如何修改?
答:选择好想要打开的视频文件后,将代码1中第7行的capture(0)括号里的内容改为想要播放的视频文件名
(2)在示例代码1第9行的while循环中,Mat是一个什么数据结构? 为什么一定要加一句waitKey延时代码,删除它行不行?
答:while 循环体中的 Mat 数据结构其实是一个点阵,对应图像上的每一个点,点的集合形成了一帧图像。waitKey(30) 中的参数单位是 ms 毫秒,也就是每一帧间隔 30 ms ,其函数功能是不断刷新图像。该语句是不能删除的,否则会执行错误,无法播放视频或录制视频。
(3)示例代码1代码会在while循环中一直运行,你如果试图用鼠标关闭图像显示窗口,会发现始终关不掉。需要用键盘Ctrl+C 强制中断程序,非常不友好。如何改进?
答:改进方法是增加一个判断语句来进行关闭。
四、总结
这次实验,学习了gcc的编译的一步编译和四个阶段的编译过程,对于程序的执行过程有了较为深切的认识,以及静态库.a文件和动态库.so文件如何生成和使用,opencv图像编程功能中图片的转换显现和视频的播放录制,总的来说,学习到的很多,及时中间多重困难,但是有了前人的帮助,最后仍是成功做出了不得不说还是有点成就感的。