参考网站http://www.cnblogs.com/surpassal/archive/2012/12/19/zed_webcam_lab1.html

一、一些知识

1、V4L和V4L2。

V4L是Linux环境下开发视频采集设备驱动程序的一套规范(API),它为驱动程序的编写提供统一的接口,并将所有的视频采集设备的驱动程序都纳入其的管理之中。V4L不仅给驱动程序编写者带来极大的方便,同时也方便了应用程序的编写和移植。V4L2是V4L的升级版,由于我们使用的OOB是3.3的内核,不再支持V4L,因而编程不再考虑V4L的api和参数定义。

2、YUYV与RGB24

RGB是一种颜色的表示法,计算机中一般采用24位来存储,每个颜色占8位。YUV也是一种颜色空间,为什么要出现YUV,主要有两个原因,一个是为了让彩色信号兼容黑白电视机,另外一个原因是为了减少传输的带宽。YUV中,Y表示亮度,U和V表示色度,总之它是将RGB信号进行了一种处理,根据人对亮度更敏感些,增加亮度的信号,减少颜色的信号,以这样“欺骗”人的眼睛的手段来节省空间。YUV到RGB颜色空间转换关系是:

R = Y + 1.042*(V-128);
G = Y - 0.34414*(U-128) - 0.71414*(V-128);
B = Y + 1.772*(U-128);

YUV的格式也很多,不过常见的就是422、420等。YUYV就是422形式,简单来说就是,两个像素点P1、P2本应该有Y1、U1、V1和Y2、U2、V2这六个分量,但是实际只保留Y1、U1、Y2、V2。

图1 YUYV像素


二、应用程序设计

先定义一些宏和结构体,方便后续编程

 #define  TRUE    1
#define FALSE 0 #define FILE_VIDEO "/dev/video0"
#define BMP "/usr/image_bmp.bmp"
#define YUV "/usr/image_yuv.yuv" #define IMAGEWIDTH 640
#define IMAGEHEIGHT 480 static int fd;
static struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt,fmtack;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
enum v4l2_buf_type type;
unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*];

其中

#define FILE_VIDEO     "/dev/video0"

是要访问的摄像头设备,默人都是/dev/video0

#define BMP          "/usr/image_bmp.bmp"
#define YUV "/usr/image_yuv.yuv"

是采集后存储的图片,为了方便测试,这里将直接获取的yuv格式数据也保存成文件,可以通过yuvviewer等查看器查看。

static   int      fd;
static struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt,fmtack;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers req;
struct v4l2_buffer buf;
enum v4l2_buf_type type;

这些结构体的定义都可以从/usr/include/linux/videodev2.h中找到定义,具体含义在后续编程会做相应解释。

#define  IMAGEWIDTH    640
#define IMAGEHEIGHT 480

为采集图像的大小。

定义一个frame_buffer,用来缓存RGB颜色数据

unsigned char frame_buffer[IMAGEWIDTH*IMAGEHEIGHT*3]

这些宏和定义结束后,就可以开始编程配置摄像头并采集图像了。一般来说V4L2采集视频数据分为五个步骤:首先,打开视频设备文件,进行视频采集的参数初始化,通过V4L2接口设置视频图像的采集窗口、采集的点阵大小和格式;其次,申请若干视频采集的帧缓冲区,并将这些帧缓冲区从内核空间映射到用户空间,便于应用程序读取/处理视频数据;第三,将申请到的帧缓冲区在视频采集输入队列排队,并启动视频采集;第四,驱动开始视频数据的采集,应用程序从视频采集输出队列取出帧缓冲区,处理完后,将帧缓冲区重新放入视频采集输入队列,循环往复采集连续的视频数据;第五,停止视频采集。在本次设计中,定义了三个函数实现对摄像头的配置和采集。

int init_v4l2(void);
int v4l2_grab(void);
int close_v4l2(void);

同时由于采集到的图像数据是YUYV格式,需要进行颜色空间转换,定义了转换函数。

int yuyv_2_rgb888(void);

下面就详细介绍这几个函数的实现。

1、初始化V4l2

(1)打开视频。linux对摄像头的访问和普通设备一样,使用open函数就可以,返回值是设备的id。

1 if ((fd = open(FILE_VIDEO, O_RDWR)) == -1)
2 {
3 printf("Error opening V4L interface\n");
4 return (FALSE);
5 }

(2)读video_capability中信息。通过调用IOCTL函数和接口命令VIDIOC_QUERYCAP查询摄像头的信息,结构体v4l2_capability中有包括驱动名称driver、card、bus_info、version以及属性capabilities。这里我们需要检查一下是否是为视频采集设备V4L2_CAP_VIDEO_CAPTURE以及是否支持流IO操作V4L2_CAP_STREAMING。

 if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -)
{
printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
return (FALSE);
}
else
{
printf("driver:\t\t%s\n",cap.driver);
printf("card:\t\t%s\n",cap.card);
printf("bus_info:\t%s\n",cap.bus_info);
printf("version:\t%d\n",cap.version);
printf("capabilities:\t%x\n",cap.capabilities); if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
{
printf("Device %s: supports capture.\n",FILE_VIDEO);
} if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
{
printf("Device %s: supports streaming.\n",FILE_VIDEO);
}
}

(3)列举摄像头所支持像素格式。使用命令VIDIOC_ENUM_FMT,获取到的信息通过结构体v4l2_fmtdesc查询。这步很关键,不同的摄像头可能支持的格式不一样,V4L2可以支持的格式很多,/usr/include/linux/videodev2.h文件中可以看到。

 fmtdesc.index=;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-)
{
printf("\t%d.%s\n",fmtdesc.index+,fmtdesc.description);
fmtdesc.index++;
}

(4)设置像素格式。一般的USB摄像头都会支持YUYV,有些还支持其他的格式。通过前一步对摄像头所支持像素格式查询,下面需要对格式进行设置。命令为VIDIOC_S_FMT,通过结构体v4l2_format把图像的像素格式设置为V4L2_PIX_FMT_YUYV,高度和宽度设置为IMAGEHEIGHT和IMAGEWIDTH。一般情况下一个摄像头所支持的格式是不可以随便更改的,我尝试把把一个只支持YUYV和MJPEG的摄像头格式改为RGB24或者JPEG,都没有成功。

fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.height = IMAGEHEIGHT;
fmt.fmt.pix.width = IMAGEWIDTH;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -)
{
printf("Unable to set format\n");
return FALSE;
}

为了确保设置的格式作用到摄像头上,再通过命令VIDIOC_G_FMT将摄像头设置读取回来。

 if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -)
{
printf("Unable to get format\n");
return FALSE;
}
{
printf("fmt.type:\t\t%d\n",fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> ) & 0xFF,(fmt.fmt.pix.pixelformat >> ) & 0xFF, (fmt.fmt.pix.pixelformat >> ) & 0xFF);
printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
}

完整的初始化代码如下:

 int init_v4l2(void)
{
int i;
int ret = ; //opendev
if ((fd = open(FILE_VIDEO, O_RDWR)) == -)
{
printf("Error opening V4L interface\n");
return (FALSE);
} //query cap
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -)
{
printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
return (FALSE);
}
else
{
printf("driver:\t\t%s\n",cap.driver);
printf("card:\t\t%s\n",cap.card);
printf("bus_info:\t%s\n",cap.bus_info);
printf("version:\t%d\n",cap.version);
printf("capabilities:\t%x\n",cap.capabilities); if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
{
printf("Device %s: supports capture.\n",FILE_VIDEO);
} if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
{
printf("Device %s: supports streaming.\n",FILE_VIDEO);
}
} //emu all support fmt
fmtdesc.index=;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:\n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-)
{
printf("\t%d.%s\n",fmtdesc.index+,fmtdesc.description);
fmtdesc.index++;
} //set fmt
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.height = IMAGEHEIGHT;
fmt.fmt.pix.width = IMAGEWIDTH;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -)
{
printf("Unable to set format\n");
return FALSE;
}
if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -)
{
printf("Unable to get format\n");
return FALSE;
}
{
printf("fmt.type:\t\t%d\n",fmt.type);
printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> ) & 0xFF,(fmt.fmt.pix.pixelformat >> ) & 0xFF, (fmt.fmt.pix.pixelformat >> ) & 0xFF);
printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
}
//set fps
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
setfps.parm.capture.timeperframe.numerator = ;
setfps.parm.capture.timeperframe.denominator = ; printf("init %s \t[OK]\n",FILE_VIDEO); return TRUE;
}

2、图像采集

(1)申请缓存区。使用参数VIDIOC_REQBUFS和结构体v4l2_requestbuffers。v4l2_requestbuffers结构中定义了缓存的数量,系统会据此申请对应数量的视频缓存。

req.count=;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_REQBUFS,&req)==-)
{
printf("request for buffers error\n"); }

(2)获取每个缓存的信息,并mmap到用户空间。定义结构体

struct buffer
{
void * start;
unsigned int length;
} * buffers;

来存储mmap后的地址信息。需要说明的是由于mmap函数定义时返回的地址是个void *,因而这里面的start也是个 void *。实际地址在运行的时候会自动分配。

 for (n_buffers = ; n_buffers < req.count; n_buffers++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
//query buffers
if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -)
{
printf("query buffer error\n");
return(FALSE);
} buffers[n_buffers].length = buf.length;
//map
buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[n_buffers].start == MAP_FAILED)
{
printf("buffer map error\n");
return(FALSE);
}
}

(3) 之后就可以开始采集视频了。使用命令VIDIOC_STREAMON。

1 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
2 ioctl (fd, VIDIOC_STREAMON, &type);

(4)取出缓存中已经采样的缓存。使用命令VIDIOC_DQBUF。视频数据存放的位置是buffers[n_buffers].start的地址处。

1 ioctl(fd, VIDIOC_DQBUF, &buf);

完整的采集代码:

 int v4l2_grab(void)
{
unsigned int n_buffers; //request for 4 buffers
req.count=;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
if(ioctl(fd,VIDIOC_REQBUFS,&req)==-)
{
printf("request for buffers error\n");
} //mmap for buffers
buffers = malloc(req.count*sizeof (*buffers));
if (!buffers)
{
printf ("Out of memory\n");
return(FALSE);
} for (n_buffers = ; n_buffers < req.count; n_buffers++)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
//query buffers
if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -)
{
printf("query buffer error\n");
return(FALSE);
} buffers[n_buffers].length = buf.length;
//map
buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (buffers[n_buffers].start == MAP_FAILED)
{
printf("buffer map error\n");
return(FALSE);
}
} //queue
for (n_buffers = ; n_buffers < req.count; n_buffers++)
{
buf.index = n_buffers;
ioctl(fd, VIDIOC_QBUF, &buf);
} type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd, VIDIOC_STREAMON, &type); ioctl(fd, VIDIOC_DQBUF, &buf); printf("grab yuyv OK\n");
return(TRUE);
}

3、YUYV转RGB24

由于摄像头采集的数据格式为YUYV,为了方便后续设计,需要转变为RGB24,并将转换完成的数据存储到frame_buffer中。值得一提的是,由于定义的时候buffers[index].start是个void *,没有办法进行+1这样的操作,需要强制转换为

char * pointer
pointer = buffers[0].start

由于后续RGB的数据要存储到BMP中,而BMP文件中颜色数据是“倒序”,即从下到上,从左到右,因而在向frame_buffer写数据时是从最后一行最左测开始写,每写满一行行数减一。

 int yuyv_2_rgb888(void)
{
int i,j;
unsigned char y1,y2,u,v;
int r1,g1,b1,r2,g2,b2;
char * pointer; pointer = buffers[].start; for(i=;i<;i++)
{
for(j=;j<;j++)
{
y1 = *( pointer + (i*+j)*);
u = *( pointer + (i*+j)* + );
y2 = *( pointer + (i*+j)* + );
v = *( pointer + (i*+j)* + ); r1 = y1 + 1.042*(v-);
g1 = y1 - 0.34414*(u-) - 0.71414*(v-);
b1 = y1 + 1.772*(u-); r2 = y2 + 1.042*(v-);
g2 = y2 - 0.34414*(u-) - 0.71414*(v-);
b2 = y2 + 1.772*(u-); if(r1>)
r1 = ;
else if(r1<)
r1 = ; if(b1>)
b1 = ;
else if(b1<)
b1 = ; if(g1>)
g1 = ;
else if(g1<)
g1 = ; if(r2>)
r2 = ;
else if(r2<)
r2 = ; if(b2>)
b2 = ;
else if(b2<)
b2 = ; if(g2>)
g2 = ;
else if(g2<)
g2 = ; *(frame_buffer + ((--i)*+j)* ) = (unsigned char)b1;
*(frame_buffer + ((--i)*+j)* + ) = (unsigned char)g1;
*(frame_buffer + ((--i)*+j)* + ) = (unsigned char)r1;
*(frame_buffer + ((--i)*+j)* + ) = (unsigned char)b2;
*(frame_buffer + ((--i)*+j)* + ) = (unsigned char)g2;
*(frame_buffer + ((--i)*+j)* + ) = (unsigned char)r2;
}
}
printf("change to RGB OK \n");
}

4、停止采集和关闭设备

使用命令VIDIOC_STREAMOFF停止视频采集,并关闭设备。

 int close_v4l2(void)
{
ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
if(fd != -)
{
close(fd);
return (TRUE);
}
return (FALSE);
}

5、主函数

需要把我们采集到图像数据存储成图片,为了方便调试,先将原始的数据存储为yuv格式文件,再将转换成RGB后的数据存储为BMP。定义BMP头结构体

 typedef struct tagBITMAPFILEHEADER{
WORD bfType; // the flag of bmp, value is "BM"
DWORD bfSize; // size BMP file ,unit is bytes
DWORD bfReserved; //
DWORD bfOffBits; // must be 54 }BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // must be 0x28
DWORD biWidth; //
DWORD biHeight; //
WORD biPlanes; // must be 1
WORD biBitCount; //
DWORD biCompression; //
DWORD biSizeImage; //
DWORD biXPelsPerMeter; //
DWORD biYPelsPerMeter; //
DWORD biClrUsed; //
DWORD biClrImportant; //
}BITMAPINFOHEADER;

完整的主函数

{

    FILE * fp1,* fp2;

    BITMAPFILEHEADER   bf;
BITMAPINFOHEADER bi; fp1 = fopen(BMP, "wb");
if(!fp1)
{
printf("open "BMP"error\n");
return(FALSE);
} fp2 = fopen(YUV, "wb");
if(!fp2)
{
printf("open "YUV"error\n");
return(FALSE);
} if(init_v4l2() == FALSE)
{
return(FALSE);
} //Set BITMAPINFOHEADER
bi.biSize = ;
bi.biWidth = IMAGEWIDTH;
bi.biHeight = IMAGEHEIGHT;
bi.biPlanes = ;
bi.biBitCount = ;
bi.biCompression = ;
bi.biSizeImage = IMAGEWIDTH*IMAGEHEIGHT*;
bi.biXPelsPerMeter = ;
bi.biYPelsPerMeter = ;
bi.biClrUsed = ;
bi.biClrImportant = ; //Set BITMAPFILEHEADER
bf.bfType = 0x4d42;
bf.bfSize = + bi.biSizeImage;
bf.bfReserved = ;
bf.bfOffBits = ; v4l2_grab();
fwrite(buffers[].start, **, , fp2);
printf("save "YUV"OK\n"); yuyv_2_rgb888();
fwrite(&bf, , , fp1);
fwrite(&bi, , , fp1);
fwrite(frame_buffer, bi.biSizeImage, , fp1);
printf("save "BMP"OK\n"); fclose(fp1);
fclose(fp2);
close_v4l2(); return(TRUE);
}

三、ARM板测试

PC上测试OK后,可以“挪”到ZedBoard上了。使用arm-xilinx-linux交叉编译环境对源文件进行交叉编译,将生成的可执行文件拷贝到ZedBoard上运行即可。

使用命令

arm-xilinx-linux-gnueabi-gcc v4l2grab.c -o zed-camera

对程序进行编译,编译通过后将生成的可执行文件zed-camera拷贝到到ZedBoard上,并将USB摄像头连接到ZedBoard上,通过命令

ls /dev 

查看dev目录下的是否有video0设备。如果有,可以运行可执行文件了。在运行前我比较习惯获得可执行文件的权限,使用命令

chmod +x zed-camera

参数+x的意思是这个文件对于当前用户是可执行的。也可以使用

chmod 777 zed-camera

这样所有用户都有读写执行的权限。使用命令

./zed-camera

执行可执行程序,程序运行,并输出以下信息:

driver: uvcvideo
card: UVC Camera (046d:0825)
bus_info: usb-xusbps-ehci.0-1.3
version: 197376
capabilities: 4000001
Device /dev/video0: supports capture.
Device /dev/video0: supports streaming.
Support format:
1.YUV 4:2:2 (YUYV)
2.MJPEG
fmt.type: 1
pix.pixelformat: YUYV
pix.height: 480
pix.width: 640
pix.field: 1
init /dev/video0 [OK]
grab yuyv OK
save /usr/image_yuv.yuv OK
change to RGB OK
save /usr/image_bmp.bmp OK

采集到的图片默认是在/usr目录下的,将其拷贝出来
cp /usr/image* /mnt
 
完整工程和代码:https://files.cnblogs.com/files/liusiluandzhangkun/lab_v4l2_yuyv.zip
可以指定任意分辨率摄像头的代码:https://files.cnblogs.com/files/liusiluandzhangkun/v4l2grab_Anysize.rar

14、USB摄像头(V4L2接口)的图片采集的更多相关文章

  1. 基于ZedBoard的Webcam设计(一):USB摄像头(V4L2接口)的图片采集【转】

    转自:http://www.cnblogs.com/surpassal/archive/2012/12/19/zed_webcam_lab1.html 一直想把USB摄像头接到Zedboard上,搭建 ...

  2. 大华摄像头报警接口中图片加密,python调用c++方式实现解密

    项目中,大华摄像头的报警信息,里面有图片地址,需要1天内取下来,保持留痕 可惜,图片下载后,加密了,大华提供了android,ios,c++例子,没有提供java解密例子 没办法,只好先用c++例子简 ...

  3. 15、USB摄像头图片采集+QT显示

    一.Qt的下载和的安装 关于Qt的安装,网络上有很详细的介绍.这里只做简单介绍. 需要的安装包一共有两个:Qt Creator 和QTE. 1)QT Creator 下载地址:qt-sdk-linux ...

  4. ffmpeg从USB摄像头采集一张原始图片(转)

    本文讲解使用ffmpeg从USB摄像头中采集一帧数据并写入文件保存,测试平台使用全志A20平台,其他平台修改交叉工具链即可移植.开发环境使用eclipse+CDT.交叉工具链使用arm-Linux-g ...

  5. 利用opencv从USB摄像头获取图片

    由于opencv自带的VideoCapture函数直接从usb摄像头获取视频数据,所以用这个来作为实时的图像来源用于实体检测识别是很方便的. 1. 安装opencv 安装的步骤可以按照之前这个文章操作 ...

  6. 使用openCV打开USB摄像头(UVC 小米micro接口)

    之前在AndroidStudio上就用了别人用写的库成功地打开了USB摄像头. 于是我之后又在PC上尝试了一下,首先去淘宝买了个MICRO母转USB公的转接口,然后在Qt上配置了一下OPENCV后开始 ...

  7. Linux下用FFMPEG采集usb摄像头到RTMP

    Linux下用 FFMPEG 采集 usb摄像头视频 和 摄像头内置麦克风音频 到RTMP服务   ffmpeg -f video4linux2 -qscale 10 -r 12 -s 640x480 ...

  8. 【Xilinx-Petalinux学习】-06-OpenCV通过USB摄像头采集图像。

    占位, 实现USB摄像头的图像采集与保存

  9. Hi3559AV100外接UVC/MJPEG相机实时采图设计(三):V4L2接口通过MPP平台输出

    可以首先参考前面两篇文章: Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析: https://www.cnblogs.com/iFrank/p/1 ...

随机推荐

  1. spark源码编译,本地调试

    1.下载源码 2.进入源码根据README.md编译源码,注意使用的是源码目录下的maven编译 3.用idea导入顶层pom文件 4.修改顶层pom文件和example下的pom文件,将scope的 ...

  2. unalias---取消命令别名

    unalias命令用来取消命令别名,是为shell内建命令. 选项 -a:取消所有命令别名. 实例 使用unalias命令将已经设置的命令别名"cc"取消,输入如下命令: unal ...

  3. linux系统常用日志

    系统日志记录着系统运行中的记录信息,在服务或者系统发生故障的时候,通过查询系统日志,可以帮助我们诊断.系统日志可以预警安全问题,系统日志一般都存放在/var/log目录下 /var/log/dmesg ...

  4. 【习题 8-3 UVA - 12545】Bits Equalizer

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 如果1的个数第一个串比第2个串多. 那么就无解. 否则. 找几个位置去凑1 优先找'?'然后才是0的位置 剩余的全都用swap操作就 ...

  5. JS-网页中分页栏

    原理 三部分 我给分页栏分成了3部分 上一页:调用prePage()函数 下一页:调用nextPage()函数 带有数字标识的部,调用skipPage()函数 prePage函数 function p ...

  6. Android NDK调试出错Unknown Application ABI, Unable to detect application ABI&#39;s的解决方式

    今天在调试Android NDK的时候,ADT的控制台报了这个错误: Unknown Application ABI, Unable to detect application ABI's 在网上查了 ...

  7. bash的启动文件

    文件名称 功能描写叙述 /etc/profile 登录时自己主动运行 ~/.bash_profile,~/.bash_login,~/.profile 登录时自己主动运行 ~/.bashrc shel ...

  8. Node.js安装+环境配置【Windows版】

    Node.js安装及环境配置之Windows篇  一.安装环境 1.本机系统:Windows 10 Pro(64位)2.Node.js:v6.9.2LTS(64位) 二.安装Node.js步骤 1.下 ...

  9. BZOJ3697: 采药人的路径(点分治)

    Description 采药人的药田是一个树状结构,每条路径上都种植着同种药材.采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的.采药人每天都要进行采药活动 ...

  10. 微信小程序实现一个简单的表格

    wxml <view class="table"> <view class="tr bg-w"> <view class=&quo ...