最近学习使用v4l2在树莓派上抓取视频图像。有些收获,现在分享给大家。

操作的过程为:1、打开摄像头;2、获取摄像头的capability(它所支持的操作)3、获取摄像头支持哪些视频/图片帧格式;4、设置视频/图片/帧格式;5、在摄像头驱动中申请缓冲空间用来临时存放抓取到的照片;6、申请程序中的内存空间并将缓冲区映射到内存中;7、开始采集视频并处理数据;8、停止视频采集并解除映射;9、关闭摄像头。

现在我写一下详细过程以及一些困惑。

1、打开摄像头

在linux中,任何设备都是以文件的形式存在于系统中的。一般而言,摄像头是/dev目录下一个名为videoN的文件,因此路径就是/dev/videoN。N的数值需要具体去查看,通过在终端输入指令 ls /dev,即可查看。

打开文件程序语句为:

int fd=open(CAMERA_DEVICE,O_RDWR,0);
if(fd == -1)
{printf("failed to open camera!\n");exit(0);
}

但是后来我使用了一个高清摄像头,貌似也是树莓派专用的,叫Raspberry Pi Camera V2。这款摄像头插上去之后还需要一些设置才能看在/dev到目录下看到video0,具体操作为:

(参考博文:https://blog.csdn.net/u010918541/article/details/70332235)

1)、开启树莓派的摄像头功能

sudo raspi-config

选择第三个。

2)、启动摄像头功能

选择第一个。

3)、重启树莓派

4)、重启之后使用如下指令打开/etc/modules文件

sudo nano /etc/modules

然后在末尾添加上 :

i2v-dev
bcm2835-v4l2  #注意v4后面是小写字母l,不是数字1

5)、关闭当前终端,重开一个终端就可以时文件生效。

2、获取摄像头的capability

struct v4l2_capability cap;
if(ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
{printf("failed to query driver's driver capability!\n");exit(0);
}
printf("cap.capabilities=0x%x\n",cap.capabilities);

其中:

1)、v4l2_capability是存储设备所支持的功能信息的结构体,原型如下:

struct v4l2_capability
{__u8 driver[16];      //驱动名。通常为uvcvideo__u8 card[32];        //Device名。通常是摄像头厂家产品名字__u8 bus_info[32];    //在Bus系统中存放位置__u32 version;        //driver 版本__u32 capabilities;   //能力集__u32 reserved[4];    //保留,暂时不用
}; 

__8表示无符号8位的数据,__32表示32位,之所以这样用,而不是直接使用int 或者unsigned int,是因为在不同应用平台上,int所表示的不一定是4字节,也有可能是两个字节。

2)、VIDIOC_QUERYCAP是获取设备能力的控制语句。在博客:https://blog.csdn.net/u011425939/article/details/53671869中有一些常用的v4l2常用的命令和结构体介绍。

3)、使用ioctl函数需要#include "linux/videodev2.h"。ioctl函数是一个功能很强大的函数,我曾试图找过它的原型和所有用法(主要是想知道该函数的第二个参数能有多少,都是哪些),但是最后也没有找全,因为不只是控制摄像头会用到这个函数,还有其他许多方面,因此也可以不用太过于纠结这个函数。

我的摄像头的能力集为:cap.capabilities=0x85200005。在头文件linux/videodev2.h中有对能力集的描述,这里列出部分主要的描述,(还有一些可以自己上网查找):

#define V4L2_CAP_VIDEO_CAPTURE 0x00000001

#define V4L2_CAP_VIDEO_OVERLAY 0x00000004

#define V4L2_CAP_READWRITE 0x01000000

#define V4L2_CAP_STREAMING 0x04000000

摄像头必须要支持V4L2_CAP_VIDEO_CAPTURE功能才能谈得上抓取视频图片。此外还至少拥有第三或第四个能力中的一种,一般是拥有第四个。最终表现出来的capability是各个能力按位或的结果。因此可以看出我的摄像头能支持上述四种功能,分别为:图像抓取,图像覆盖(具体作用可以查看博客:https://blog.csdn.net/naibei/article/details/81146642),读写功能,流。

3、获取摄像头支持哪些视频/图片帧格式

struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("image format(s) driver supported:\n");
do{ioctl_error=ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc);printf("index:%d,flag:%d, format name:%s,pixelformat=0x%x \n",fmtdesc.index,fmtdesc.flags,fmtdesc.description,fmtdesc.pixelformat);fmtdesc.index++;
}while(0==ioctl_error);

其中:

1)、v4l2_fmtdesc是存储摄像头所支持的格式信息的结构体,原型为:

struct v4l2_fmtdesc{u32 index;                //要查询的格式序号,应用程序设置enum v4l2_buf_type type;  //帧类型,应用程序设置u32 flags;                //是否为压缩格式u8 description[32];       //格式名称u32 pixelformat;          //格式u32 reserved[4];          //保留};

2)、type一般为V4L2_BUF_TYPE_VIDEO_CAPTURE,也可以为V4L2_BUF_TYPE_VIDEO_OUTPUT,后一种的没用过,还不知道是干嘛用的。

3)、VIDIOC_ENUM_FMT是查询摄像头支持的图片格式。

我的摄像头所支持的视频/图片格式有:

4、设置视频/图片/帧格式

struct v4l2_format format;
memset(&format,0,sizeof(struct v4l2_format));format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.fmt.pix.width = VIDEO_WIDTH; //width of frame photo
format.fmt.pix.height = VIDEO_HEIGHT;//hight of frame photo
format.fmt.pix.pixelformat=VIDEO_FORMAT;//frame formatioctl_error=ioctl(fd, VIDIOC_TRY_FMT, &format);
printf("ioctl_errorurn value of VIDIOC_TRY_FMT=%d\n",ioctl_error);ioctl_error=ioctl(fd, VIDIOC_S_FMT, &format);
printf("ioctl_errorurn value of VIDIOC_S_FMT=%d\n",ioctl_error);

其中:

1)、v4l2_format是存储帧格式信息的结构体,原型为:

struct v4l2_format {  enum v4l2_buf_type type;union {  struct v4l2_pix_format pix;//V4L2_BUF_TYPE_VIDEO_CAPTUREstruct v4l2_window win;//V4L2_BUF_TYPE_VIDEO_OVERLAYstruct v4l2_vbi_format vbi;//V4L2_BUF_TYPE_VBI_CAPTUREstruct v4l2_sliced_vbi_format sliced;//V4L2_BUF_TYPE_SLICED_VBI_CAPTURE__u8 raw_data[200];//user-defined}fmt;  };  

union中的结构体v4l2_pix_format原型为:

struct v4l2_pix_format {  __u32 width;//宽,必须是16的倍数__u32 height;//高,必须是16的倍数__u32 pixelformat;//视频数据存储类型enum v4l2_field field;  __u32  bytesperline;//for padding, zero if unused__u32 sizeimage;  enum v4l2_colorspace colorspace;  __u32 priv;//private data, depends on pixelformat};

2)、type一般为V4L2_BUF_TYPE_VIDEO_CAPTURE;

3)、format.fmt.pix.width和format.fmt.pix.height分别表示帧的宽和高

4)、format.fmt.pix.pixelformat是图片的格式,也就是“3、获取视频/图片支持哪些帧格式”查询后所支持的格式。它的计算过程可以查看博文:https://blog.csdn.net/myarrow/article/details/8516266。这是linux中典型的结构体中有结构体的情况。

我这里有一点疑惑,我的摄像头支持很多格式,但是除了“JFIF JPEG”格式外,其余格式抓取的图片均显示已损坏。

5)、VIDIOC_TRY_FMT是在验证设置的格式是否正确,若成功则返回0。

6)、VIDIOC_S_FMT是在设置格式,若成功设置,则返回0。

5、在摄像头驱动中申请缓冲空间用来临时存放抓取到的照片

struct v4l2_requestbuffers req; //buffer in devicereq.count = BUFFER_COUNT;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;if(ioctl(fd,VIDIOC_REQBUFS,&req) == -1) //VIDIOC_REQBUFS means // allocate memory from device
{printf("failed to apply memory for images from catching video!\n");exit(0);
}

其中:

1)、v4l2_requestbuffers是记录需要在设备中申请的缓冲区的信息的结构体。原型为:

struct v4l2_requestbuffers{__u32 count;// 缓存数量,也就是说在缓存队列里保持多少张照片,一般 //少于5张enum v4l2_buf_type type;// 数据流类型,必须永远是 //4L2_BUF_TYPE_VIDEO_CAPTUREenum v4l2_memory memory;// V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR__u32 reserved[2];};enum v4l2_memory {  V4L2_MEMORY_MMAP = 1,  V4L2_MEMORY_USERPTR = 2,  V4L2_MEMORY_OVERLAY = 3};

2)、VIDIOC_REQBUFS表示从设备中分配内存。

6、申请程序中的内存空间并将缓冲区映射到内存中

为了使应用程序能够使用抓取到的图片,因此在应用程序中也申请相应大小的内存空间。因为程序不能直接对设备缓冲区的内容直接进行操作

VideoBuffer* video_buf = calloc(req.count, sizeof(*video_buf));
struct v4l2_buffer buf;int numBufs=0;
for (numBufs = 0; numBufs < req.count; numBufs++)
{memset( &buf, 0, sizeof(buf) );buf.index = numBufs;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;//apply buffer memory from deviceif (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1){ printf("faild to get buffer memory%d\n",numBufs);exit(0);}    video_buf[numBufs].length = buf.length;// map device buffer into program memory so that we can get information //of image(s) and display in monitorvideo_buf[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);if (video_buf[numBufs].start == MAP_FAILED){printf("video_buf[%d] ",numBufs);perror("map error:");exit(0);}//queue buffer to save images from deviceif (ioctl(fd, VIDIOC_QBUF, &buf) == -1){printf("fiald to push into buffer queue\n");exit(0);}printf("Frame buffer %d: address=0x%x, length=%d\n", numBufs, (unsigned int)video_buf[numBufs].start, video_buf[numBufs].length);
}

其中:

1)、VideoBuffer是一个需要自己定义的结构体,是申请的内存的外部接口。原型为:

typedef struct VideoBuffer
{void   *start;size_t  length;
}VideoBuffer;

2)、v4l2_buffer,存储设备缓冲帧的数据,原型为:

struct v4l2_buffer
{u32 index; //buffer 序号enum v4l2_buf_type type; //buffer 类型u32 byteused; //buffer 中已使用的字节数u32 flags; // 区分是MMAP 还是USERPTRenum v4l2_field field;struct timeval timestamp; // 获取第一个字节时的系统时间struct v4l2_timecode timecode;u32 sequence; // 队列中的序号enum v4l2_memory memory; //IO 方式,被应用程序设置union m{u32 offset; // 缓冲帧地址,只对MMAP 有效unsigned long userptr;};u32 length; // 缓冲帧长度u32 input;u32 reserved;};

3)、VIDIOC_QUERYBUF是将VIDIOC_REQBUFS中分配的数据缓存转换成物理地址。

4)、mmap 将设备中的缓冲区映射为程序中使用的(虚拟)内存。具体使用,可参见博文:https://blog.csdn.net/yangle4695/article/details/52139585

5)、VIDIOC_QBUF 把记录缓冲区数据的结构体放入缓存队列中,以便记录图像帧。

7、开始采集视频并处理数据

enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl_error = ioctl(fd, VIDIOC_STREAMON, &type);//VIDIOC_STREAMON://start command:video captuer,then  the device program will start colloct video data and save it into device's bufferif (ioctl_error < 0)
{printf("VIDIOC_STREAMON failed (%d)\n", ioctl_error);exit(0);
}int loop_i;
for(loop_i=0;loop_i<DESIRED_IMAGE_COUNT;loop_i++)
{// Get frameioctl_error = ioctl(fd, VIDIOC_DQBUF, &buf);//get a photo from device and save into device buffer,because of mapping before,it also saved into program memory if (ioctl_error < 0)
{printf("VIDIOC_DQBUF failed (%d)\n", ioctl_error);exit(0);
}// save frame
strcpy(image_path,INITIAL_IMAGE_PATH);//Re-initialise  path name of image
get_image_name(image_path,loop_i+1);//get new path nameFILE *fp = fopen(image_path, "wb");
if (fp < 0)
{printf("open frame data file failed\n");exit(0);
}fwrite(video_buf[buf.index].start, 1, buf.length, fp);
fclose(fp);printf("Capture frame%d saved in %s\n",loop_i+1,image_path);// Re-queen buffer
ioctl_error = ioctl(fd, VIDIOC_QBUF, &buf);
if (ioctl_error < 0)
{printf("VIDIOC_QBUF failed (%d)\n", ioctl_error);exit(0);
}

其中:

1)、type应为V4L2_BUF_TYPE_VIDEO_CAPTURE。

2)、VIDIOC_STREAMON 是开始采集视频数据的命令

3)、DESIRED_IMAGE_COUNT 自己的宏定义,需要获取的图片数量

4)、VIDIOC_QBUF 将出队的缓冲帧重新放入队列中,以便再次获得

5)、文件的命名,是按照获取的时间来命名的,因为1秒钟能获取多张,因此还得加上后缀1、2、3...之类的。

8、停止视频采集并解除映射

for (numBufs=0; numBufs< req.count; numBufs++)
{munmap(video_buf[numBufs].start, video_buf[numBufs].length);
}

9、关闭摄像头

if(fd != -1)
{if(close(fd) != -1)printf("succeed to close camera!\n");
}else
{printf("failed to close camera!\n");exit(0);
}

附上程序源代码:

#include "stdio.h"
#include "fcntl.h"
#include "linux/videodev2.h"
#include "string.h"
#include "stdlib.h"
#include "sys/mman.h"
#include "errno.h"
#include "time.h"#define CAMERA_DEVICE "/dev/video0"#define INITIAL_IMAGE_PATH "/home/Photos/"
#define VIDEO_WIDTH 1366
#define VIDEO_HEIGHT 768
#define VIDEO_FORMAT V4L2_PIX_FMT_JPEG
#define BUFFER_COUNT 2  //image count in device buffer queue
#define DESIRED_IMAGE_COUNT 5typedef struct VideoBuffer
{void   *start;size_t  length;
}VideoBuffer;//get each image's path name via different catching time
void get_image_name(char _str[],int _num)
{char time_str[30];time_t timep;struct tm *p;time(&timep);p=localtime(&timep);sprintf(time_str,"%d%d%d_%d_%d_%d_%d",(1900+p->tm_year), (1+p->tm_mon),p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec,_num);strcat(time_str,".jpg");strcat(_str,time_str);
}void main()
{int fd=0; //camera handelint ioctl_error=0; //returned value of function: inctl char image_path[80]=INITIAL_IMAGE_PATH;// open file of camera   printf("#open camera:\n");fd=open(CAMERA_DEVICE,O_RDWR,0);if(fd == -1){printf("failed to open camera!\n");exit(0);}printf("fd=%d\n",fd);//query driver's capabilityprintf("\n#query driver's capability:\n");struct v4l2_capability cap;if(ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1){printf("failed to query driver's driver capability!\n");exit(0);}printf("cap.capabilities=0x%x\n",cap.capabilities);//enumerate image format(s) driver supportedprintf("\n#enumerate image format(s) driver supported:\n");struct v4l2_fmtdesc fmtdesc;fmtdesc.index=0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;printf("image format(s) driver supported:\n");do{ioctl_error=ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc);printf("index:%d,flag:%d, format name:%s,pixelformat=0x%x \n",fmtdesc.index,fmtdesc.flags,fmtdesc.description,fmtdesc.pixelformat);fmtdesc.index++;}while(0==ioctl_error);//set frame formatprintf("\n#set frame format:\n");struct v4l2_format format;memset(&format,0,sizeof(struct v4l2_format));format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;format.fmt.pix.width = VIDEO_WIDTH; //width of frame photoformat.fmt.pix.height = VIDEO_HEIGHT;//hight of frame photoformat.fmt.pix.pixelformat=VIDEO_FORMAT;//frame formatioctl_error=ioctl(fd, VIDIOC_TRY_FMT, &format);printf("ioctl_errorurn value of VIDIOC_TRY_FMT=%d\n",ioctl_error);ioctl_error=ioctl(fd, VIDIOC_S_FMT, &format);printf("ioctl_errorurn value of VIDIOC_S_FMT=%d\n",ioctl_error);//apply memory for images from catching videoprintf("\n#apply memory for images from catching video:\n");struct v4l2_requestbuffers req; //buffer in devicereq.count = BUFFER_COUNT;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if(ioctl(fd,VIDIOC_REQBUFS,&req) == -1) //VIDIOC_REQBUFS means allocate memory from device{printf("failed to apply memory for images from catching video!\n");exit(0);}//apply phycisal(program) memory and map it to memory of imagesprintf("\n#apply phycisal memory and map it to memory of images:\n");VideoBuffer* video_buf = calloc(req.count, sizeof(*video_buf));struct v4l2_buffer buf;int numBufs=0;for (numBufs = 0; numBufs < req.count; numBufs++) {memset( &buf, 0, sizeof(buf) );buf.index = numBufs;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;//apply buffer memory from deviceif (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {printf("faild to get buffer memory%d\n",numBufs);exit(0);}video_buf[numBufs].length = buf.length;// map device buffer into program memory so that we can get information of image(s) and display in monitorvideo_buf[numBufs].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,MAP_SHARED,fd, buf.m.offset);if (video_buf[numBufs].start == MAP_FAILED) {printf("video_buf[%d] ",numBufs);perror("map error:");exit(0);}//queue buffer to save images from deviceif (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {printf("fiald to push into buffer queue\n");exit(0);}printf("Frame buffer %d: address=0x%x, length=%d\n", numBufs, (unsigned int)video_buf[numBufs].start, video_buf[numBufs].length);}//start to record videoprintf("\n#start to record video:\n");enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl_error = ioctl(fd, VIDIOC_STREAMON, &type);//VIDIOC_STREAMON: start command:video captuer,then  the device program will start //colloct video data and save it into device's bufferif (ioctl_error < 0) {printf("VIDIOC_STREAMON failed (%d)\n", ioctl_error);exit(0);}int loop_i;for(loop_i=0;loop_i<DESIRED_IMAGE_COUNT;loop_i++){// Get frameioctl_error = ioctl(fd, VIDIOC_DQBUF, &buf);//get a photo from device and save into device buffer,because of mapping before,//it also saved into program memory if (ioctl_error < 0){printf("VIDIOC_DQBUF failed (%d)\n", ioctl_error);exit(0);}// save frame strcpy(image_path,INITIAL_IMAGE_PATH);//Re-initialise path name of imageget_image_name(image_path,loop_i+1);//get new path nameFILE *fp = fopen(image_path, "wb");if (fp < 0) {printf("open frame data file failed\n");exit(0);}fwrite(video_buf[buf.index].start, 1, buf.length, fp);fclose(fp);printf("Capture frame%d saved in %s\n",loop_i+1,image_path);// Re-queen bufferioctl_error = ioctl(fd, VIDIOC_QBUF, &buf);if (ioctl_error < 0){printf("VIDIOC_QBUF failed (%d)\n", ioctl_error);exit(0);}}// Release the resourcefor (numBufs=0; numBufs< req.count; numBufs++) {munmap(video_buf[numBufs].start, video_buf[numBufs].length);}//close file of cameraprintf("\n#close camera:\n");if(fd != -1){         if(close(fd) != -1)printf("succeed to close camera!\n");}else{printf("failed to close camera!\n");exit(0);}//free memory free(Video_buf);}

参考博客及文章:

1、Raspberry Pi Camera V2 的设置:https://blog.csdn.net/u010918541/article/details/70332235

2、overlay显示模式:https://blog.csdn.net/naibei/article/details/81146642

3、V4L2常用命令标志符和结构体:https://blog.csdn.net/u011425939/article/details/53671869

4、V4L2 pixel format:https://blog.csdn.net/myarrow/article/details/8516266 、http://blog.chinaunix.net/uid-22666248-id-279978.html

5、Linux 内存映射函数 mmap()函数详解:https://blog.csdn.net/yangle4695/article/details/52139585

6、基于Linux的v4l2视频架构驱动编写:https://www.linuxidc.com/Linux/2011-03/33022p4.htm

7、采用V4L2读取的USB摄像头:https://blog.csdn.net/lucykingljj/article/details/41804221

8、V4L2应用程序框架:http://www.cnblogs.com/hzhida/archive/2012/05/29/2524397.html

使用v4l2在树莓派上抓取视频图像相关推荐

  1. 利用Python在Jetson TX2上抓取和显示摄像头影像

    小编来自水下机器人社区193369905,里面小编给出了很多资料 在本贴中,小编"我"分享了如何使用python 代码(及 OpenCV)在Jetson TX2上抓取和显示摄像头影 ...

  2. 强大的chrome(1)以acfun为例抓取视频

    chrome很强大,很强大,很强大. 想要了解他的强大呢,就先要掌握一些基本的chrome命令. 1. chrome://flags   可用来启用或者关闭某些chrome的体验特性   2. chr ...

  3. Python抓取视频内容

    Python抓取视频内容 Python 是一种面向对象.解释型计算机程序设计语言,由Guido van Rossum于1989年底发明,第一个公开发行版发行于1991年.Python语法简洁而清晰,具 ...

  4. 用python爬虫下载视频_使用Python编写简单网络爬虫抓取视频下载资源

    我第一次接触爬虫这东西是在今年的5月份,当时写了一个博客搜索引擎,所用到的爬虫也挺智能的,起码比电影来了这个站用到的爬虫水平高多了! 回到用Python写爬虫的话题. Python一直是我主要使用的脚 ...

  5. 从Internet上抓取指定URL的源码的方案

    从Internet上抓取指定URL的源码的方案 作者: 引言: 在做无线项目的时候,与通讯公司的数据通讯有一部分是通过XML交互的,所以必须要动态抓取通讯公司提供的固定的Internet上的数据,便研 ...

  6. 根据专利号到专利查询的网站上抓取想要的信息(上)

    前述:前几天看到有人论要请别人写一个从从网页上抓取某个专利号的收费信息的一个程序,说实话我自己知道那里面的原理是什么,但一直没有自己动手实现以下.根据自己的实际的工作需要一般是有一张Excel表,第一 ...

  7. 从计算机屏幕上抓取动态操作过程 也称为,计算机学业水平考试单项选择题综合训练一 答案复习过程...

    精品文档 计算机学业水平考试 单项选择题综合训练(一)[答案] 1.显示器最主要的性能指标是( D ). A.显示器品牌 B.显示器颜色 C.显示器大小 D.显示器分辨率 2.用"两画图&q ...

  8. 根据专利号到专利查询的网站上抓取想要的信息(下)

    上一回讲了怎么根据一个专利号发送我们要查询信息的请求,详情请看根据专利号到专利查询的网站上抓取想要的信息(上).接下来要做的就是从一个Excel表中去读取我们要查的一系列的申请号,然后将抓到的信息写到 ...

  9. 在网易财经上抓取股价信息

    1.在网易财经上抓取股票价格信息 代码: """ date:2018-7-31 function:在网易财经上抓取股价信息(仅选取2016年举办的业绩说明会信息) aut ...

最新文章

  1. 语音合成模块 文本转TTS 真人发音 SYN6288
  2. C#中提示:当前上下文中不存在名称“ConfigurationManager”
  3. “许巍日”新歌提前曝光 《爱如少年》10/15温暖登场!
  4. c语言 想输入非数字是报错误,C语言上机练习5C言上机练习5.doc
  5. 十大经典排序算法系列
  6. jqGrid 操作一些总结(二)
  7. 高一计算机算法教案,高中信息技术 算法及其实现 教案
  8. 基于MATLAB的列车防护曲线组合步长算法分析与仿真验证
  9. 一个全栈工程师要掌握哪些技能
  10. 联想拯救者笔记本加固态硬盘过程重点
  11. 用ARCGIS做DEM地形分析
  12. c word to html 走样,打印机打印效果走样解决办法.pptx
  13. 华硕主板反复进入BIOS以及无法识别固态硬盘?
  14. 交换机crc错误是什么意思_OSN1800设备LDX对接S9706交换机,交换机持续有CRC错误告警...
  15. 荣耀magic3pro和华为p50pro对比哪个好
  16. esc键 qt 退出菜单_Qt中Esc键触发事件处理
  17. 使用浏览器访问服务器端页面
  18. Openlayers利用kriging.js实现纯前端插值
  19. 一张照片,AI生成抽象画(CLIPasso项目安装使用) | 机器学习
  20. 【读书笔记】用技术人的眼光看世界

热门文章

  1. 财务报表分析实务(第三讲)
  2. 测试项目经理推荐的Java 并发测试神器
  3. 租房变欠贷?这“租金贷”连法院工作人员都被套路了
  4. mint-ui 各个组件示例
  5. 中国移动社交行业有多厉害,现在就让你知晓!
  6. unity安卓应用名称多语言本地化
  7. 信息安全培训到底培什么
  8. 《被讨厌的勇气》读后感
  9. 高并发大型互联网站架构设计
  10. 平安科技联络云荣膺金融科技最佳云联络中心服务商