攝像頭應用編程(四):ARM Linux LCD實時預覽UVC攝像頭畫面

文章目錄

  • 1、前言
  • 2、環境介紹
  • 3、步驟
  • 4、應用程序編寫
    • 4.1、lcd初始化
    • 4.2、攝像頭初始化
    • 4.3、jpeg解碼
    • 4.4、開啟攝像頭
    • 4.5、完整的程序如下
  • 5、測試
    • 5.1、編譯應用程序
    • 5.2、運行應用程序
  • 6、總結

1、前言

本次應用程序主要針對支持MJPEG格式輸出的UVC攝像頭。

2、環境介紹

rk3566 + 7寸 mipi lcd + uvc攝像頭

3、步驟

應用程序編寫主要分為以下幾個步驟:

1、lcd初始化。

2、攝像頭初始化。

3、攝像頭采集數據。

4、jpeg解碼。

5、lcd顯示。

4、應用程序編寫

4.1、lcd初始化

typedef struct lcd_mes {int fd;unsigned char *fb_base;int lcd_width;int lcd_height;unsigned int bpp;unsigned int line_width;
} lcd_mes;int lcd_init(const char *fb_dev, lcd_mes *lcd)
{int screen_size;struct fb_var_screeninfo var;   if (fb_dev == NULL)goto _err;/* 1. open /dev/fb* */    lcd->fd = open(fb_dev, O_RDWR);if(lcd->fd < 0){printf("can not open %s\n", fb_dev);goto _err;}/* 2. get lcd message */if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &var)){printf("can not get var\n");goto _err;}screen_size = var.xres * var.yres * var.bits_per_pixel / 8;lcd->line_width  = var.xres * var.bits_per_pixel / 8;lcd->lcd_width = var.xres;lcd->lcd_height = var.yres;lcd->bpp = var.bits_per_pixel;lcd->fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);if (lcd->fb_base == (unsigned char *)-1){printf("can not mmap\n");goto _err;}memset(lcd->fb_base, 0x00, screen_size);return 0;_err:return -1;
}

4.2、攝像頭初始化

使用v4l2接口初始化uvc攝像頭:

typedef struct camera_mes {int fd;void *bufs[32];int bufs_index;int buf_length;char fmt[20];int frame_x_size;int frame_y_size;
} camera_mes;int camera_init(const char *video, camera_mes *camera)
{   struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum fsenum;int fmt_index = 0;int frame_index = 0;int buf_cnt;int i;if (video == NULL)goto _err;/* 1. open /dev/video* */camera->fd = open(video, O_RDWR);if (camera->fd < 0){printf("can not open %s\n", video);goto _err;}/* 2. query capability */struct v4l2_capability cap;memset(&cap, 0, sizeof(struct v4l2_capability));if (0 == ioctl(camera->fd, VIDIOC_QUERYCAP, &cap)){        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {fprintf(stderr, "Error opening device %s: video capture not supported.\n", video);goto _ioc_querycap_err;}if(!(cap.capabilities & V4L2_CAP_STREAMING)) {fprintf(stderr, "%s does not support streaming i/o\n", video);goto _ioc_querycap_err;}}else{printf("can not get capability\n");goto _ioc_querycap_err;}/* 3. enum formt */while (1){fmtdesc.index = fmt_index;  fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  if (0 != ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmtdesc))break;frame_index = 0;// printf("format %s,%d:\n", fmtdesc.description, fmtdesc.pixelformat);while (1){memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;/* get framesize */if (ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0){// printf("\t%d: %d x %d\n", frame_index, fsenum.discrete.width, fsenum.discrete.height);}else{break;}frame_index++;}fmt_index++;}/* 4. set formt */struct v4l2_format fmt;memset(&fmt, 0, sizeof(struct v4l2_format));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = camera->frame_x_size;fmt.fmt.pix.height = camera->frame_y_size;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;fmt.fmt.pix.field = V4L2_FIELD_ANY;if (0 == ioctl(camera->fd, VIDIOC_S_FMT, &fmt)){// printf("the final frame-size has been set : %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);camera->frame_x_size = fmt.fmt.pix.width;camera->frame_y_size = fmt.fmt.pix.height;strncpy(camera->fmt, "Motion-JPEG", strlen("Motion-JPEG"));}else{printf("can not set format\n");goto _ioc_sfmt_err;}/* 5. require buffer */struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(camera->fd, VIDIOC_REQBUFS, &rb)){buf_cnt = rb.count;for(i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(camera->fd, VIDIOC_QUERYBUF, &buf)){/* mmap */camera->bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->fd, buf.m.offset);if(camera->bufs[i] == MAP_FAILED) {printf("Unable to map buffer");goto _err;}}else{printf("can not query buffer\n");goto _err;}            }}else{printf("can not request buffers\n");goto _ioc_reqbufs_err;}/* 6. queue buffer */for(i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(camera->fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");goto _ioc_qbuf_err;}}camera->bufs_index = 0;     // init camera structcamera->buf_length = 0;return 0;_ioc_qbuf_err:
_ioc_reqbufs_err:
_ioc_sfmt_err:
_ioc_querycap_err:
_err:return -1;
}

4.3、jpeg解碼

int jpeg_show_on_lcd(lcd_mes *lcd, camera_mes *camera)
{int min_width, min_height;int valid_bytes;int offset_x, offset_y;struct jpeg_decompress_struct cinfo;struct jpeg_error_mgr jerr;cinfo.err = jpeg_std_error(&jerr);      // 錯誤處理對象與解碼對象綁定jpeg_create_decompress(&cinfo);         // 初始化解碼器jpeg_mem_src(&cinfo, camera->bufs[camera->bufs_index], camera->buf_length);   // 指定JPEG數據的來源jpeg_read_header(&cinfo, TRUE);         // 讀取圖像信息cinfo.out_color_space = JCS_RGB;        // 設置解碼后的顏色空間為RGBjpeg_start_decompress(&cinfo);          // 開始解碼unsigned char *jpeg_line_buf = (char *)malloc(cinfo.output_components * cinfo.output_width);    // 用于存儲從JPEG解碼器讀取的一行數據unsigned int *fb_line_buf = (int *)malloc(lcd->line_width);                                     // 用于存儲轉換后的RGB數據,準備寫入framebuffermin_width = (cinfo.output_width < lcd->lcd_width) ? cinfo.output_width : lcd->lcd_width;min_height = (cinfo.output_height < lcd->lcd_height) ? cinfo.output_height : lcd->lcd_height;valid_bytes = min_width * lcd->bpp / 8;             // 一行的有效字節數unsigned char *ptr = lcd->fb_base;offset_x = ((lcd->lcd_width - min_width) / 2) * lcd->bpp / 8;   // x方向居中offset_y = (lcd->lcd_height - min_height) / 2;      // y方向居中for (int i = 0; i < offset_y; i++)                  ptr += lcd->lcd_width * lcd->bpp / 8;unsigned int red, green, blue;unsigned int color;while (cinfo.output_scanline < min_height){jpeg_read_scanlines(&cinfo, &jpeg_line_buf, 1); // 每次讀取一行for(int i = 0; i < min_width; i++)              {red = jpeg_line_buf[i * 3];                 green = jpeg_line_buf[i * 3 + 1];           blue = jpeg_line_buf[i * 3 + 2];            color = red << 16 | green << 8 | blue;      // RGB888轉RGB8888fb_line_buf[i] = color;}memcpy(ptr + offset_x, fb_line_buf, valid_bytes);   // 將一行數據寫入framebufferptr += lcd->lcd_width * lcd->bpp / 8;               // 移動到下一行}jpeg_finish_decompress(&cinfo);                     // 完成解碼jpeg_destroy_decompress(&cinfo);                    // 銷毀解碼對象free(jpeg_line_buf);                                // 釋放內存free(fb_line_buf);                                  // 釋放內存return 0;
}

4.4、開啟攝像頭

int main(int argc, char **argv)
{.../* start camera */if (0 != ioctl(camera.fd, VIDIOC_STREAMON, &type)){printf("Unable to start capture\n");goto _err;}printf("\nstart camera ...\n");while (1){/* poll */memset(fds, 0, sizeof(fds));fds[0].fd = camera.fd;fds[0].events = POLLIN;if (1 == poll(fds, 1, -1)){/* dequeue buffer */struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(camera.fd, VIDIOC_DQBUF, &buf)){printf("Unable to dequeue buffer\n");goto _ioc_dqbuf_err;}/* jpeg show on lcd */camera.bufs_index = buf.index;camera.buf_length = buf.length;jpeg_show_on_lcd(&lcd, &camera);/* queue buffer */if (0 != ioctl(camera.fd, VIDIOC_QBUF, &buf)){printf("Unable to queue buffer");goto _ioc_qbuf_err;}}}...}

4.5、完整的程序如下


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>          
#include <linux/videodev2.h>
#include <poll.h>
#include <sys/mman.h>
#include <jpeglib.h>
#include <linux/fb.h>typedef struct lcd_mes {int fd;unsigned char *fb_base;int lcd_width;int lcd_height;unsigned int bpp;unsigned int line_width;
} lcd_mes;typedef struct camera_mes {int fd;void *bufs[32];int bufs_index;int buf_length;char fmt[20];int frame_x_size;int frame_y_size;
} camera_mes;int jpeg_show_on_lcd(lcd_mes *lcd, camera_mes *camera)
{int min_width, min_height;int valid_bytes;int offset_x, offset_y;struct jpeg_decompress_struct cinfo;struct jpeg_error_mgr jerr;cinfo.err = jpeg_std_error(&jerr);      // 錯誤處理對象與解碼對象綁定jpeg_create_decompress(&cinfo);         // 初始化解碼器jpeg_mem_src(&cinfo, camera->bufs[camera->bufs_index], camera->buf_length);   // 指定JPEG數據的來源jpeg_read_header(&cinfo, TRUE);         // 讀取圖像信息cinfo.out_color_space = JCS_RGB;        // 設置解碼后的顏色空間為RGBjpeg_start_decompress(&cinfo);          // 開始解碼unsigned char *jpeg_line_buf = (char *)malloc(cinfo.output_components * cinfo.output_width);    // 用于存儲從JPEG解碼器讀取的一行數據unsigned int *fb_line_buf = (int *)malloc(lcd->line_width);                                     // 用于存儲轉換后的RGB數據,準備寫入framebuffermin_width = (cinfo.output_width < lcd->lcd_width) ? cinfo.output_width : lcd->lcd_width;min_height = (cinfo.output_height < lcd->lcd_height) ? cinfo.output_height : lcd->lcd_height;valid_bytes = min_width * lcd->bpp / 8;             // 一行的有效字節數unsigned char *ptr = lcd->fb_base;offset_x = ((lcd->lcd_width - min_width) / 2) * lcd->bpp / 8;   // x方向居中offset_y = (lcd->lcd_height - min_height) / 2;      // y方向居中for (int i = 0; i < offset_y; i++)                  ptr += lcd->lcd_width * lcd->bpp / 8;unsigned int red, green, blue;unsigned int color;while (cinfo.output_scanline < min_height){jpeg_read_scanlines(&cinfo, &jpeg_line_buf, 1); // 每次讀取一行for(int i = 0; i < min_width; i++)              {red = jpeg_line_buf[i * 3];                 green = jpeg_line_buf[i * 3 + 1];           blue = jpeg_line_buf[i * 3 + 2];            color = red << 16 | green << 8 | blue;      // RGB888轉RGB8888fb_line_buf[i] = color;}memcpy(ptr + offset_x, fb_line_buf, valid_bytes);   // 將一行數據寫入framebufferptr += lcd->lcd_width * lcd->bpp / 8;               // 移動到下一行}jpeg_finish_decompress(&cinfo);                     // 完成解碼jpeg_destroy_decompress(&cinfo);                    // 銷毀解碼對象free(jpeg_line_buf);                                // 釋放內存free(fb_line_buf);                                  // 釋放內存return 0;
}int lcd_init(const char *fb_dev, lcd_mes *lcd)
{int screen_size;struct fb_var_screeninfo var;   if (fb_dev == NULL)goto _err;/* 1. open /dev/fb* */    lcd->fd = open(fb_dev, O_RDWR);if(lcd->fd < 0){printf("can not open %s\n", fb_dev);goto _err;}/* 2. get lcd message */if (ioctl(lcd->fd, FBIOGET_VSCREENINFO, &var)){printf("can not get var\n");goto _err;}screen_size = var.xres * var.yres * var.bits_per_pixel / 8;lcd->line_width  = var.xres * var.bits_per_pixel / 8;lcd->lcd_width = var.xres;lcd->lcd_height = var.yres;lcd->bpp = var.bits_per_pixel;lcd->fb_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, lcd->fd, 0);if (lcd->fb_base == (unsigned char *)-1){printf("can not mmap\n");goto _err;}memset(lcd->fb_base, 0x00, screen_size);return 0;_err:return -1;
}int camera_init(const char *video, camera_mes *camera)
{   struct v4l2_fmtdesc fmtdesc;struct v4l2_frmsizeenum fsenum;int fmt_index = 0;int frame_index = 0;int buf_cnt;int i;if (video == NULL)goto _err;/* 1. open /dev/video* */camera->fd = open(video, O_RDWR);if (camera->fd < 0){printf("can not open %s\n", video);goto _err;}/* 2. query capability */struct v4l2_capability cap;memset(&cap, 0, sizeof(struct v4l2_capability));if (0 == ioctl(camera->fd, VIDIOC_QUERYCAP, &cap)){        if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {fprintf(stderr, "Error opening device %s: video capture not supported.\n", video);goto _ioc_querycap_err;}if(!(cap.capabilities & V4L2_CAP_STREAMING)) {fprintf(stderr, "%s does not support streaming i/o\n", video);goto _ioc_querycap_err;}}else{printf("can not get capability\n");goto _ioc_querycap_err;}/* 3. enum formt */while (1){fmtdesc.index = fmt_index;  fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;  if (0 != ioctl(camera->fd, VIDIOC_ENUM_FMT, &fmtdesc))break;frame_index = 0;// printf("format %s,%d:\n", fmtdesc.description, fmtdesc.pixelformat);while (1){memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = fmtdesc.pixelformat;fsenum.index = frame_index;/* get framesize */if (ioctl(camera->fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0){// printf("\t%d: %d x %d\n", frame_index, fsenum.discrete.width, fsenum.discrete.height);}else{break;}frame_index++;}fmt_index++;}/* 4. set formt */struct v4l2_format fmt;memset(&fmt, 0, sizeof(struct v4l2_format));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = camera->frame_x_size;fmt.fmt.pix.height = camera->frame_y_size;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;fmt.fmt.pix.field = V4L2_FIELD_ANY;if (0 == ioctl(camera->fd, VIDIOC_S_FMT, &fmt)){// printf("the final frame-size has been set : %d x %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height);camera->frame_x_size = fmt.fmt.pix.width;camera->frame_y_size = fmt.fmt.pix.height;strncpy(camera->fmt, "Motion-JPEG", strlen("Motion-JPEG"));}else{printf("can not set format\n");goto _ioc_sfmt_err;}/* 5. require buffer */struct v4l2_requestbuffers rb;memset(&rb, 0, sizeof(struct v4l2_requestbuffers));rb.count = 32;rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;rb.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(camera->fd, VIDIOC_REQBUFS, &rb)){buf_cnt = rb.count;for(i = 0; i < rb.count; i++) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 == ioctl(camera->fd, VIDIOC_QUERYBUF, &buf)){/* mmap */camera->bufs[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera->fd, buf.m.offset);if(camera->bufs[i] == MAP_FAILED) {printf("Unable to map buffer");goto _err;}}else{printf("can not query buffer\n");goto _err;}            }}else{printf("can not request buffers\n");goto _ioc_reqbufs_err;}/* 6. queue buffer */for(i = 0; i < buf_cnt; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index = i;buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(camera->fd, VIDIOC_QBUF, &buf)){perror("Unable to queue buffer");goto _ioc_qbuf_err;}}camera->bufs_index = 0;     // init camera structcamera->buf_length = 0;return 0;_ioc_qbuf_err:
_ioc_reqbufs_err:
_ioc_sfmt_err:
_ioc_querycap_err:
_err:return -1;
}int main(int argc, char **argv)
{int ret;lcd_mes lcd;camera_mes camera;int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;struct pollfd fds[1];if (argc != 3){printf("Usage: %s </dev/videoX> </dev/fbX>\n", argv[0]);return -1;}/* lcd init */ret = lcd_init(argv[2], &lcd);if (ret == -1){printf("lcd init err !\n");goto _err;}printf("\n-------------- lcd message --------------\n");printf("screen pixel: %d x %d\n", lcd.lcd_width, lcd.lcd_height);printf("line width: %d (byte)\n", lcd.line_width);printf("bpp: %d\n", lcd.bpp);printf("-----------------------------------------\n");/* camera init */camera.frame_x_size = lcd.lcd_width;camera.frame_y_size = lcd.lcd_height;ret = camera_init(argv[1], &camera);if (ret == -1){printf("camera init err !\n");goto _err;}printf("\n------------ camera message -------------\n");printf("frame size: %d x %d\n", camera.frame_x_size, camera.frame_y_size);printf("format: %s\n", camera.fmt);printf("-----------------------------------------\n");/* start camera */if (0 != ioctl(camera.fd, VIDIOC_STREAMON, &type)){printf("Unable to start capture\n");goto _err;}printf("\nstart camera ...\n");while (1){/* poll */memset(fds, 0, sizeof(fds));fds[0].fd = camera.fd;fds[0].events = POLLIN;if (1 == poll(fds, 1, -1)){/* dequeue buffer */struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;if (0 != ioctl(camera.fd, VIDIOC_DQBUF, &buf)){printf("Unable to dequeue buffer\n");goto _ioc_dqbuf_err;}/* jpeg show on lcd */camera.bufs_index = buf.index;camera.buf_length = buf.length;jpeg_show_on_lcd(&lcd, &camera);/* queue buffer */if (0 != ioctl(camera.fd, VIDIOC_QBUF, &buf)){printf("Unable to queue buffer");goto _ioc_qbuf_err;}}}/* close camera */if (0 != ioctl(camera.fd, VIDIOC_STREAMOFF, &type)){printf("Unable to stop capture\n");goto _ioc_streamoff_err;}close(camera.fd);return 0;_ioc_streamoff_err:
_ioc_qbuf_err:
_ioc_dqbuf_err:
_err:return -1;
}

5、測試

5.1、編譯應用程序

如果你使用的buildroot系統,需要交叉編譯。

這里測試所使用的板子跑的是ubuntu,執行如下命令直接編譯:

sudo gcc -o uvctolcd uvctolcd.c -ljpeg

5.2、運行應用程序

sudo ./uvctolcd /dev/video10 /dev/fb0

6、總結

參考文章:Linux攝像頭(v4l2應用)——在LCD上實時顯示攝像頭采集JPEG數據_v4l2調用并顯示圖像-CSDN博客
源碼gitee倉庫:

倉庫主頁:
https://gitee.com/cattle_l/v4l2_app.git
直接拉取:
git clone https://gitee.com/cattle_l/v4l2_app.git

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/71219.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71219.shtml
英文地址,請注明出處:http://en.pswp.cn/web/71219.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

藍橋與力扣刷題(藍橋 k倍區間)

題目&#xff1a;給定一個長度為 N 的數列&#xff0c;A1,A2,?AN?&#xff0c;如果其中一段連續的子序列 Ai,Ai1,?Aj( i≤j ) 之和是 K 的倍數&#xff0c;我們就稱這個區間[i,j] 是 K 倍區間。 你能求出數列中總共有多少個 K 倍區間嗎&#xff1f; 輸入描述 第一行包含兩…

json介紹、python數據和json數據的相互轉換

目錄 一 json介紹 json是什么&#xff1f; 用處 Json 和 XML 對比 各語言對Json的支持情況 Json規范詳解 二 python數據和json數據的相互轉換 dumps() : 轉換成json loads(): 轉換成python數據 總結 一 json介紹 json是什么&#xff1f; 實質上是一條字符串 是一種…

PAT乙級真題 / 知識點(1)

引言&#xff1a; 起初&#xff0c;報PAT是伙伴推薦。但在報名路途中&#xff0c;有朋友說&#xff0c;花時間到這上面不值得&#xff0c;還有學長說沒聽過&#xff0c;野雞杯。 我一笑而過&#xff0c;我可能就是偏執&#xff0c;我就是想報。隨著刷真題&#xff0c;我的基礎…

單細胞分析(20)——inferCNV分析

InferCNV分析筆記 1. 分析目標 InferCNV&#xff08;Inference of Copy Number Variations&#xff09;是一種基于單細胞轉錄組數據推斷**拷貝數變異&#xff08;CNV&#xff09;**的方法&#xff0c;推測其基因組變異情況。 2. 數據準備 2.1 載入數據 library(Seurat) set…

C++:多態與虛函數

1.虛函數&#xff0c;在函數前加virtual即可。有虛函數時&#xff0c;父類指針指向父類對象時就會使用父類的成員&#xff0c;指向子類對象時就可以使用子類成員&#xff0c;進而我們引入了多態的概念。 2.多態&#xff1a;父類指針指向子類的對象&#xff0c;通過父類指針調用…

WSL下使用git克隆失敗解決

WSL默認nat模式&#xff0c;別動了防火墻放行&#xff0c;見圖1git導入[bash1]&#xff0c;ip為你wsl上linxu通過ifconfig獲取的本機ip&#xff0c;端口對好某alcsh軟件開啟tun模式【經過測試&#xff0c;不開也行】應該成了&#xff0c;如果不行&#xff0c;修改.wslconfig為下…

開放鴻蒙OpenHarmony 5.0.0 Release 兼容性測試實戰經驗分享

OpenHarmony 5.0版本的發布時間是2024年12月20日至21日。這個版本帶來了許多新特性和改進。現在5.0出了兩個release 版本&#xff0c;分別是5.0.0和5.0.1。 就在5.0版本發布不到2周的時間內&#xff0c;2025年01月01日起&#xff0c;不支持新產品基于老分支&#xff08;OpenHar…

C++中explicit關鍵字的含義以及用法

在C中&#xff0c;explicit關鍵字用于修飾構造函數和轉換運算符&#xff08;C11起&#xff09;&#xff0c;防止編譯器進行隱式類型轉換&#xff0c;要求必須顯式調用構造函數或轉換操作。以下是其核心用法和示例&#xff1a; 1. 修飾構造函數 用途 禁止隱式構造對象&#xf…

Oracle OCP認證考試考點詳解083系列01

題記&#xff1a; 本系列主要講解Oracle OCP認證考試考點&#xff08;題目&#xff09;&#xff0c;適用于19C/21C,跟著學OCP考試必過。 1. 第1題&#xff1a; 題目 解析及答案&#xff1a; 關于自動工作量存儲庫&#xff08;AWR&#xff09;快照&#xff0c;以下哪三個選項…

從DNS到TCP:DNS解析流程和瀏覽器輸入域名訪問流程

1 DNS 解析流程 1.1 什么是DNS域名解析 在生活中我們會經常遇到域名&#xff0c;比如說CSDN的域名www.csdn.net&#xff0c;百度的域名www.baidu.com,我們也會碰到IP&#xff0c;現在目前有的是IPV4&#xff0c;IPV6。那這兩個有什么區別呢&#xff1f;IP地址是互聯網上計算機…

《2025軟件測試工程師面試》接口測試篇

基礎概念 什么是接口測試? 接口測試是測試系統組件間接口的一種測試,主要用于檢測外部系統和內部系統之間以及各個子系統之間的交互點。測試的重點是檢查數據的交換、傳遞和控制管理的過程,以及系統間的相互邏輯依賴關系等。 接口測試的優勢是什么? 接口測試具有規范性與擴…

【PHP腳本語言詳解】為什么直接訪問PHP文件會顯示空白?從錯誤示例到正確執行!

前言 作為一名開發者&#xff0c;你是否曾經遇到過這樣的問題&#xff1a;寫了一個PHP腳本&#xff0c;放到服務器根目錄后&#xff0c;直接通過file:///路徑訪問卻顯示空白頁面&#xff1f;而換成http://localhost卻能正常顯示&#xff1f;這篇文章將帶你深入理解PHP腳本語言…

word轉換為pdf后圖片失真解決辦法、高質量PDF轉換方法

1、安裝Adobe Acrobat Pro DC 自行安裝 2、配置Acrobat PDFMaker &#xff08;1&#xff09;點擊word選項卡上的Acrobat插件&#xff0c;&#xff08;2&#xff09;點擊“首選項”按鈕&#xff0c;&#xff08;3&#xff09;點擊“高級配置”按鈕&#xff08;4&#xff09;點…

基于PyTorch的深度學習2——逐元素操作,歸并,比較,矩陣

以下為部分逐元素操作代碼實例。 import torcht torch.randn(1, 3) t1 torch.randn(3, 1) t2 torch.randn(1, 3)#t0.1*(t1/t2) torch.addcdiv(t, 0.1, t1, t2)#計算sigmoid torch.sigmoid(t)#將t限制在[0,1]之間 torch.clamp(t,0,1)#t2進行就地運算 t.add_(2) 歸并操作一般…

線程池的工作流程

線程池的工作流程主要包括任務提交、線程分配、任務執行和線程回收等環節&#xff0c;以下是對其詳細的描述&#xff1a; 任務提交 當有任務需要執行時&#xff0c;用戶通過線程池提供的提交方法&#xff0c;如execute()或submit()方法&#xff0c;將任務&#xff08;通常是實現…

C++20 標準化有符號整數:邁向更可預測的整數運算

文章目錄 一、背景&#xff1a;為什么需要標準化&#xff1f;二、2 的補碼&#xff1a;原理與優勢&#xff08;一&#xff09;2 的補碼原理&#xff08;二&#xff09;2 的補碼的優勢 三、C20 的變化&#xff1a;明確 2 的補碼四、如何利用這一特性優化代碼&#xff08;一&…

Libgdx游戲開發系列教程(1)——環境配置及demo運行

目錄 環境 JDK環境 項目創建 1.下載gdx-setup.jar文件 2.填寫信息,創建項目 demo運行步驟 1.修改gradle.properties 2.安裝Build-Tool 3.運行 Libgdx游戲是基于Java的一款游戲引擎,可以發布Android,桌面端,Html,IOS等游戲,出名的《殺戮尖塔》也是用了此引擎制作的 本…

【jenkins配置記錄】

全局工具配置&#xff1a; D:\Program Files\Java\jdk1.8.0_281 D:\Program Files\Git\bin\git.exe E:\allure-2.13.2 2. GIT 3. 定時任務 H 8 * * 1-5 4. 構建觸發器 5. 構建后操作 Allure Report 吐血記錄&#xff1a;報告路徑可以為 workspace 相對路徑 6. 系統配置 em…

C++:內聯函數

內聯函數可以用來降低運行時間&#xff0c;當內聯函數收到編譯器的指示時&#xff0c;即可發生內聯&#xff1a;編譯器會使用函數的定義體來替代函數調用語句&#xff0c;此行為發生在編譯階段。 定義內聯函數涉及的關鍵字為inline&#xff0c;例如&#xff1a; inline void …

Linux網絡相關概念和重要知識(1)(網絡協議、網絡通信)

目錄 1.網絡協議 &#xff08;1&#xff09;網絡的起源 &#xff08;2&#xff09;為什么需要協議 &#xff08;3&#xff09;協議分層及其設計的解耦 &#xff08;4&#xff09;OSI定義的七層網絡模型 ①分層及其功能 ②TCP/IP協議 ③傳輸層協議&#xff08;TCP和UDP&a…