LVGL图像导入和解码
LVGL版本:8.1
概述
在LVGL中,可以导入多种不同类型的图像:
- 经转换器生成的C语言数组,适用于页面中不常改变的固定图像。
- 存储系统中的外部图像,比较灵活,可以通过插卡或从网络中获取,但需要配置相应的驱动和解码器,解码过程也需要占用时间和内存。
- SYMBOL类型的文本,和label组件类似。
其中第三种比较容易实现,第一种仅需经过官方工具生成二进制数据,再在代码中引用即可。本文重点说明第二种情况即外部图像的使用,并以PNG类型图片为例介绍LVGL图像的解码和显示。
原理框图
图像的解析和使用可以分以下几层:
图中的虚线框可有可无,有的仅代表多一层函数,为了更好的理解,在图上加入这些虚线框。
对于一个通过lv_img_create()函数创建的lv_img_t图像,可以通过设置图像源进行图像导入,LVGL会先查看缓存,如果有命中则直接使用,否则进入解码环节。对于内置图像如SYMBOL和VARIABLE(即C数组)类型,可以直接使用内置的解码器,在编译时会直接合并入程序。对于外部图像格式如jpg和png,则需要添加额外的解码器,并且,由于这些外部图像通常是以文件的形式存放在文件系统中,还需要提供基本的文件操作来读取文件,如Linux可以简单使用POSIX标准的系统调用函数(open、read、write等),事实上,如果没有提供文件系统,用户也可以自定义图像的打开和读取方式,只要符合接口标准即可。
开启配置
以PNG类型的图像为例,需要在lv_conf.h配置文件中,打开LV_USE_PNG开关,同时要开启文件系统(根据实际所在系统进行选择),比如在这里我选择的是LV_USE_FS_POSIX,即使用POSIX标准的文件操作函数(read、write等),也是Linux的原生操作。注意这里要求填入一个前缀来区分不同系统,通常使用类似“盘符”的字母(如这里使用了'S'),因为LVGL底层打开源路径时也是比对的路径首字母,配对成功后才会使用对应的驱动来打开。
//lv_conf.h
/* 文件系统,如开启,请设置相应的驱动字母(或前缀) */
#define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/
//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */#define LV_USE_FS_POSIX 'S' /*Uses open, read, etc*/
//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */#define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/
//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */#define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*//*PNG decoder library*/
#define LV_USE_PNG 1
此外,还可以增加系统的图像缓存数量LV_IMG_CACHE_DEF_SIZE,来减少不必要的解码,提高渲染效率。
//lv_conf.h
#define LV_IMG_CACHE_DEF_SIZE 1 //图像缓存数量,只有使用外部解码器的图像,该配置才有意义
图像解码
通过lv_img_set_src()函数设置图像源来开启LVGL解码和显示的过程,其中图像源src可以是以下类型:
- 图像路径,通常以一个盘符为首(如'S'或'S:'),后面跟绝对路径,比如是'S/img/abc.png',注意图像要预先导入到设备对应路径上才能读取;
- LVGL规定的SYMBOL类型,也可以通过官方工具生成自定义SYMBOL;
- C语言数组,包含图像的位置和色彩信息,可以通过官方工具将其它类型的图像转换为LVGL可识别的数据文件,并在使用处进行声明和导入。
在这里,该函数仅用于设置图像源,并获取图像基本信息,还没有到解码部分。
//lv_img.c
void lv_img_set_src(lv_obj_t * obj, const void * src)
{LV_ASSERT_OBJ(obj, MY_CLASS);lv_obj_invalidate(obj);lv_img_src_t src_type = lv_img_src_get_type(src); //获取来源类型lv_img_t * img = (lv_img_t *)obj;/*If the new source type is unknown free the memories of the old source*/if(src_type == LV_IMG_SRC_UNKNOWN) {LV_LOG_WARN("lv_img_set_src: unknown image type");if(img->src_type == LV_IMG_SRC_SYMBOL || img->src_type == LV_IMG_SRC_FILE) {lv_mem_free((void *)img->src);}img->src = NULL;img->src_type = LV_IMG_SRC_UNKNOWN;return;}lv_img_header_t header;lv_img_decoder_get_info(src, &header); //通过图片来源获取对应的解码器/*Save the source*/if(src_type == LV_IMG_SRC_VARIABLE) {/*If memory was allocated because of the previous `src_type` then free it*/if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_SYMBOL) {lv_mem_free((void *)img->src);}img->src = src;}else if(src_type == LV_IMG_SRC_FILE || src_type == LV_IMG_SRC_SYMBOL) {/*If the new and the old src are the same then it was only a refresh.*/if(img->src != src) {const void * old_src = NULL;/*If memory was allocated because of the previous `src_type` then save its pointer and free after allocation.*It's important to allocate first to be sure the new data will be on a new address.*Else `img_cache` wouldn't see the change in source.*/if(img->src_type == LV_IMG_SRC_FILE || img->src_type == LV_IMG_SRC_SYMBOL) {old_src = img->src;}char * new_str = lv_mem_alloc(strlen(src) + 1);LV_ASSERT_MALLOC(new_str);if(new_str == NULL) return;strcpy(new_str, src);img->src = new_str;if(old_src) lv_mem_free((void *)old_src);}}if(src_type == LV_IMG_SRC_SYMBOL) {/*`lv_img_dsc_get_info` couldn't set the with and height of a font so set it here*/const lv_font_t * font = lv_obj_get_style_text_font(obj, LV_PART_MAIN);lv_coord_t letter_space = lv_obj_get_style_text_letter_space(obj, LV_PART_MAIN);lv_coord_t line_space = lv_obj_get_style_text_line_space(obj, LV_PART_MAIN);lv_point_t size;lv_txt_get_size(&size, src, font, letter_space, line_space, LV_COORD_MAX, LV_TEXT_FLAG_NONE);header.w = size.x;header.h = size.y;}img->src_type = src_type; //图像类型(变量/符号/文件路径)img->w = header.w; //图像宽度img->h = header.h; //图像高度img->cf = header.cf; //图像色彩信息img->pivot.x = header.w / 2;img->pivot.y = header.h / 2;lv_obj_refresh_self_size(obj);/*Provide enough room for the rotated corners*/if(img->angle || img->zoom != LV_IMG_ZOOM_NONE) lv_obj_refresh_ext_draw_size(obj);lv_obj_invalidate(obj); //标记无效区域,下次更新才会正式解码图片
}
其中lv_img_decoder_get_info()用于获取图像源的基本信息,该函数会首先遍历解码器链表,匹配合适的解码器,然后将图像的宽、高、色彩信息记录下来。
//lv_img_decoder.c
lv_res_t lv_img_decoder_get_info(const void * src, lv_img_header_t * header)
{lv_memset_00(header, sizeof(lv_img_header_t));if(src == NULL) return LV_RES_INV;lv_img_src_t src_type = lv_img_src_get_type(src);if(src_type == LV_IMG_SRC_VARIABLE) {const lv_img_dsc_t * img_dsc = src;if(img_dsc->data == NULL) return LV_RES_INV;}lv_res_t res = LV_RES_INV;lv_img_decoder_t * d;_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), d) { //遍历解码器链表,为图像源寻找合适的解码器if(d->info_cb) {res = d->info_cb(d, src, header); //调用info回调,获取图像信息,header用于保存这些信息if(res == LV_RES_OK) break; //返回OK,说明解码器配对成功,退出并返回}}return res;
}
如果定义了LV_USE_PNG,则默认注册LVGL自带的PNG解码器,其中包含获取基本信息的回调函数decoder_info()。
//lv_png.c
static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
{(void) decoder; /*Unused*/lv_img_src_t src_type = lv_img_src_get_type(src); /* 获取来源类型 *//* 检查是否是文件路径类型 */if(src_type == LV_IMG_SRC_FILE) {const char * fn = src;if(!strcmp(&fn[strlen(fn) - 3], "png")) { /* 检查后缀是否为png *//* Read the width and height from the file. They have a constant location:* [16..23]: width* [24..27]: height*/uint32_t size[2];lv_fs_file_t f;lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD); //使用对应的驱动打开文件if(res != LV_FS_RES_OK) return LV_RES_INV;lv_fs_seek(&f, 16, LV_FS_SEEK_SET);uint32_t rn;lv_fs_read(&f, &size, 8, &rn);if(rn != 8) return LV_RES_INV;lv_fs_close(&f);/*Save the data in the header*/header->always_zero = 0;header->cf = LV_IMG_CF_RAW_ALPHA;/*The width and height are stored in Big endian format so convert them to little endian*/header->w = (lv_coord_t) ((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8);header->h = (lv_coord_t) ((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8);return LV_RES_OK;}}/*If it's a PNG file in a C array...*/else if(src_type == LV_IMG_SRC_VARIABLE) {const lv_img_dsc_t * img_dsc = src;header->always_zero = 0;header->cf = img_dsc->header.cf; /*Save the color format*/header->w = img_dsc->header.w; /*Save the color width*/header->h = img_dsc->header.h; /*Save the color height*/return LV_RES_OK;}return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/
}
其中lv_fs_open()函数是寻找合适的驱动来打开这个文件,我们在前面定义了LV_USE_FS_POSIX,LVGL在初始化阶段就会自动注册POSIX标准驱动,使用open、read、write等函数打开对应文件。
//lv_fs_posix.c
void lv_fs_posix_init(void)
{static lv_fs_drv_t fs_drv; /* 驱动描述子 */lv_fs_drv_init(&fs_drv);fs_drv.letter = LV_USE_FS_POSIX; //文件系统前缀,可以简单设置为'S'fs_drv.open_cb = fs_open;fs_drv.close_cb = fs_close;fs_drv.read_cb = fs_read;fs_drv.write_cb = fs_write;fs_drv.seek_cb = fs_seek;fs_drv.tell_cb = fs_tell;fs_drv.dir_close_cb = fs_dir_close;fs_drv.dir_open_cb = fs_dir_open;fs_drv.dir_read_cb = fs_dir_read;lv_fs_drv_register(&fs_drv);
}
到这里为止仅仅只是把图像源记录下来,直到下次更新周期的绘制阶段,才会正式解析图像。下面直接跳转到图像的绘制函数lv_draw_img_core(),这里仅截取解码图像的代码片段。
//lv_draw_img.c
LV_ATTRIBUTE_FAST_MEM static lv_res_t lv_img_draw_core(const lv_area_t * coords, const lv_area_t * clip_area,const void * src,const lv_draw_img_dsc_t * draw_dsc)
{if(draw_dsc->opa <= LV_OPA_MIN) return LV_RES_OK;_lv_img_cache_entry_t * cdsc = _lv_img_cache_open(src, draw_dsc->recolor, draw_dsc->frame_id);if(cdsc == NULL) return LV_RES_INV;/*......*/
}
可以看到,LVGL在绘制图像前先询问缓存,即_lv_img_cache_open()函数,该函数会先遍历缓存链表,查找是否存在匹配的缓存,如匹配直接返回该缓存,否则从链表中找一个存活时间最短的缓存,用新的图像替换掉该旧缓存。
//lv_img_cache.c
_lv_img_cache_entry_t * _lv_img_cache_open(const void * src, lv_color_t color, int32_t frame_id)
{_lv_img_cache_entry_t * cached_src = NULL;#if LV_IMG_CACHE_DEF_SIZEif(entry_cnt == 0) {LV_LOG_WARN("lv_img_cache_open: the cache size is 0");return NULL;}_lv_img_cache_entry_t * cache = LV_GC_ROOT(_lv_img_cache_array);/*Decrement all lifes. Make the entries older*/uint16_t i;for(i = 0; i < entry_cnt; i++) {if(cache[i].life > INT32_MIN + LV_IMG_CACHE_AGING) {cache[i].life -= LV_IMG_CACHE_AGING;}}for(i = 0; i < entry_cnt; i++) {if(color.full == cache[i].dec_dsc.color.full &&frame_id == cache[i].dec_dsc.frame_id &&lv_img_cache_match(src, cache[i].dec_dsc.src)) { //是否命中缓存/* 打开一个图像也会增加图像的存活时间,因此在这里对缓存增加一个time_to_open的时间 */cached_src = &cache[i];cached_src->life += cached_src->dec_dsc.time_to_open * LV_IMG_CACHE_LIFE_GAIN;if(cached_src->life > LV_IMG_CACHE_LIFE_LIMIT) cached_src->life = LV_IMG_CACHE_LIFE_LIMIT;LV_LOG_TRACE("image source found in the cache");break;}}/* 命中缓存后直接返回该缓存 */if(cached_src) return cached_src;/* 如果没有命中,则在缓存中寻找一个合适的位置存放新图像 */cached_src = &cache[0];for(i = 1; i < entry_cnt; i++) {if(cache[i].life < cached_src->life) { //寻找生存时间最短的图像cached_src = &cache[i];}}/* 找到被替代的缓存后,要先关闭它的解码器 */if(cached_src->dec_dsc.src) {lv_img_decoder_close(&cached_src->dec_dsc);LV_LOG_INFO("image draw: cache miss, close and reuse an entry");}else {LV_LOG_INFO("image draw: cache miss, cached to an empty entry");}
#elsecached_src = &LV_GC_ROOT(_lv_img_cache_single);
#endif/* 打开一个新的解码器 */uint32_t t_start = lv_tick_get();lv_res_t open_res = lv_img_decoder_open(&cached_src->dec_dsc, src, color, frame_id);if(open_res == LV_RES_INV) {LV_LOG_WARN("Image draw cannot open the image resource");lv_memset_00(cached_src, sizeof(_lv_img_cache_entry_t));cached_src->life = INT32_MIN; /*Make the empty entry very "weak" to force its us*/return NULL;}cached_src->life = 0; //记录缓存后,设置存活时间从0开始/* 记录解码它的时间 */if(cached_src->dec_dsc.time_to_open == 0) {cached_src->dec_dsc.time_to_open = lv_tick_elaps(t_start);}if(cached_src->dec_dsc.time_to_open == 0) cached_src->dec_dsc.time_to_open = 1;return cached_src;
}
当没有命中缓存时,会调用lv_img_decoder_open()函数尝试使用解码器打开该图像,打开成功后,会将图像数据提取出来。
//lv_img_decoder.c
lv_res_t lv_img_decoder_open(lv_img_decoder_dsc_t * dsc, const void * src, lv_color_t color, int32_t frame_id)
{lv_memset_00(dsc, sizeof(lv_img_decoder_dsc_t));if(src == NULL) return LV_RES_INV;lv_img_src_t src_type = lv_img_src_get_type(src);if(src_type == LV_IMG_SRC_VARIABLE) {const lv_img_dsc_t * img_dsc = src;if(img_dsc->data == NULL) return LV_RES_INV;}dsc->color = color;dsc->src_type = src_type;dsc->frame_id = frame_id;if(dsc->src_type == LV_IMG_SRC_FILE) {size_t fnlen = strlen(src);dsc->src = lv_mem_alloc(fnlen + 1);LV_ASSERT_MALLOC(dsc->src);if(dsc->src == NULL) {LV_LOG_WARN("lv_img_decoder_open: out of memory");return LV_RES_INV;}strcpy((char *)dsc->src, src);}else {dsc->src = src;}lv_res_t res = LV_RES_INV;lv_img_decoder_t * decoder;_LV_LL_READ(&LV_GC_ROOT(_lv_img_decoder_ll), decoder) { //遍历解码器链表/* info和open函数是必要的 */if(decoder->info_cb == NULL || decoder->open_cb == NULL) continue;res = decoder->info_cb(decoder, src, &dsc->header);if(res != LV_RES_OK) continue; //返回OK,说明命中解码器dsc->decoder = decoder;res = decoder->open_cb(decoder, dsc); //使用解码器打开图像文件/* 如果返回OK,表示成功打开图像文件,则返回 */if(res == LV_RES_OK) return res;/* 准备遍历下一个解码器节点 */lv_memset_00(&dsc->header, sizeof(lv_img_header_t));dsc->error_msg = NULL;dsc->img_data = NULL;dsc->user_data = NULL;dsc->time_to_open = 0;}if(dsc->src_type == LV_IMG_SRC_FILE)lv_mem_free((void *)dsc->src);return res;
}
接下来就是使用对应解码器的open函数来获取图像,这里就不进一步说明了。可以参考官方对于不同格式图像的解码器例程代码。
相关文章:
LVGL图像导入和解码
LVGL版本:8.1 概述 在LVGL中,可以导入多种不同类型的图像: 经转换器生成的C语言数组,适用于页面中不常改变的固定图像。存储系统中的外部图像,比较灵活,可以通过插卡或从网络中获取,但需要配置…...
Win10无法上网:Windows 无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目找不到域 TEST 的域控制器DNS 解析存在问题
目录 一.先看问题 二.解决问题 三.补充备用 一.先看问题 Win08有网且已经加入域 Win10无网并且找不到域(说明:Win10我之前已经加入过域的,并且能够上网,但每次在宿舍和教室切换校园网,就会导致只有Win10无网&#…...
开疆智能Canopen转Profinet网关连接工博士GBS20机器人配置案例
本案例是介绍将支持canopen通信协议的机器人机器人接入到西门子Profinet网络中,由于两种协议不能直接通讯,故选择了canopen转Profinet网关进行通讯协议转换。 配置过程: 首先打开Profinet主站配置软件,新建项目并导入网关GSD文件…...
物业企业绩效考核制度与考核体系
物业企业绩效考核制度旨在通过建立科学、公正的绩效管理体系,提升员工的工作效率、激发团队的潜力,并通过对绩效结果的合理运用来推动公司可持续发展。该制度覆盖了公司全体员工,并明确规定了不同岗位、不同部门的考核内容、周期以及绩效考核的标准操作流程。通过月度、季度…...
expo多网络请求设定。
在使用 npx expo start 启动 Expo 开发服务器时,你可以通过设置网络模式来控制你的应用如何连接到开发服务器。Expo 提供了几种网络模式供你选择: LAN (Default): 这是默认模式。在这种模式下,你的应用会通过本地局域网 (LAN) 连接到你的开发…...
M0基础篇之ADC
本节课使用到的例程 一、例程基本配置的解释 在例程中我们只使用到了PA25这一个通道,因此我们使用的是Single这个模式,也就是我们在配置模式的时候使用的是单一转换。 进行多个通道的测量我们可以使用Sequence这个模式。 二、例程基本代码讲解 DL_ADC12_…...
Cadence 高速系统设计流程及工具使用三
5.8 约束规则的应用 5.8.1 层次化约束关系 在应用约束规则之前,我们首先要了解这些约束规则是如何作用在 Cadence 设计对象上的。Cadence 中对设计对象的划分和概念,如表 5-11 所示。 在 Cadence 系统中,把设计对象按层次进行了划分&#…...
gitkraken 使用教程
一、安装教程 安装6.5.3,之后是收费的,Windows版免安装 二、使用教程 0. 软件说明 gitkraken是一个git本地仓库管理软件,可以管理多个仓库,并且仓库可以属于多个网站多个账户。 1. 克隆仓库 选择要克隆到什么位置࿰…...
抖音视频上传功能测试全维度拆解——从基础功能到隐藏缺陷的深度挖掘
一、核心功能测试(Happy Path) 文件基础验证 支持格式:MP4/MOV/AVI等(含H.264/H.265编码组合验证) 分辨率兼容性:720p→8K的渐进式测试(重点验证竖屏9:16适配) 时长边界࿱…...
基于PE环境搭建及调试S32K312
0、简介 本文基于S32K312 介绍PE的使用流程,主要是记录开发流程: MCU:NXP S32k312-100pin 编辑器:S32 Design Studio for S32 Platform 3.5 仿真器:PE USB Multilink Universal REV-E PE和jlink不一样,…...
Autoware播放提示音
播放提示音 1、修改sound_player.yaml src/autoware/utilities/sound_player/scripts/sound_player.yaml start : ~/Autoware/install/sound_player/share/sound_player/start.wav stop : ~/Autoware/install/sound_player/share/sound_player/stop.wav red …...
学习黑客5 分钟深入浅出理解cron [特殊字符]
5 分钟深入浅出理解cron 🕒 大家好!今天我们将探索Linux系统中的cron——这个强大的定时任务调度工具,它允许用户自动执行周期性任务。在网络安全领域,尤其是在TryHackMe平台上的CTF挑战中,理解cron不仅是系统管理的基…...
Qt解决自定义窗口样式不生效问题
方法一: this->setAttribute(Qt::WA_StyledBackground, true); 方法二: 将类继承QWidget 改成继承 QFrame class MyWidget : public QFrame {} 方法三:重新实现QWidget的paintEvent函数时,使用QStylePainter绘制。 void p…...
redis未授权访问
redis是高速缓存型数据库,主要用户缓存一些频繁使用的数据来缓解数据库的访问压力。而redis未授权访问漏洞是因为redis数据库使用的过程中没有设定密码,任何人都可以直接连接数据库,这既是未授权访问,这是个通用漏洞,部…...
.Net HttpClient 使用准则
HttpClient 使用准则 System.Net.Http.HttpClient 类用于发送 HTTP 请求以及从 URI 所标识的资源接收 HTTP 响应。 HttpClient 实例是应用于该实例执行的所有请求的设置集合,每个实例使用自身的连接池,该池将其请求与其他请求隔离开来。 从 .NET Core …...
Eclipse 插件开发 6 右键菜单
Eclipse 插件开发 6 右键菜单 1 plugin.xml2 SampleHandler.java3 Activator.java 1 plugin.xml <?xml version"1.0" encoding"UTF-8"?> <?eclipse version"3.4"?> <plugin><!-- 定义命令 --><extension point&…...
MGP-STR:用于场景文本识别的多粒度预测
摘要 场景文本识别(Scene Text Recognition,简称STR)多年来一直是计算机视觉领域的研究热点。为了解决这一具有挑战性的问题,研究者们陆续提出了许多创新方法,近期将语言知识引入STR模型已成为一项重要趋势。在本研究…...
DAMA语境关系图汇总及考前须知
写在前面 1.考前须知 2.梳理彩色详细的语境关系图,方便理解与深化 1.考前须知 单选题10道题,每题1分,满分10分, 多选题15道题,每题2分,满分30分, 解答题6道,每题10分ÿ…...
Vue.js框架的优缺点
别再让才华被埋没,别再让github 项目蒙尘!github star 请点击 GitHub 在线专业服务直通车GitHub赋能精灵 - 艾米莉,立即加入这场席卷全球开发者的星光革命!若你有快速提升github Star github 加星数的需求,访问taimili…...
【Pandas】pandas DataFrame corr
Pandas2.2 DataFrame Computations descriptive stats 方法描述DataFrame.abs()用于返回 DataFrame 中每个元素的绝对值DataFrame.all([axis, bool_only, skipna])用于判断 DataFrame 中是否所有元素在指定轴上都为 TrueDataFrame.any(*[, axis, bool_only, skipna])用于判断…...
【金仓数据库征文】金融行业中的国产化数据库替代应用实践
【引言】 随着国内技术的进步,越来越多的金融机构开始尝试将传统的商业数据库替换为国产化数据库。金仓数据库(KingbaseES,简称KES)凭借其高性能、稳定性和灵活的架构,逐步成为金融行业数据库替代的首选方案。本文将探…...
《基于人工智能的智能客服系统:技术与实践》
一、引言 在数字化时代,客户服务已成为企业竞争的关键领域之一。随着人工智能(AI)技术的飞速发展,智能客服系统逐渐成为企业提升服务质量和效率的重要工具。智能客服不仅能够快速响应客户咨询,还能通过自然语言处理&am…...
关于汇编语言与程序设计——单总线温度采集与显示的应用
一、实验要求 (1)握码管的使用方式 (2)掌握DS18B20温度传感器的工作原理 (3)掌握单总线通信方式实现 MCU与DS18B20数据传输 二、设计思路 1.整体思路 通过编写数码管显示程序和单总线温度采集程序,结合温度传感报警,利用手指触碰传感器,当…...
管道-验证和转换
管道-验证和转换 什么是管道管道的简单使用验证转换ParseIntPipeParseArrayPipe其他代码进度什么是管道 英雄联盟的老鼠说过一句话,条条管道通我家。管道一的脏水流到了管道二,管道二的脏水由于太脏了有杂物,堵住了去管道三的入口,所以通过不了(验证),去了管道四净化了下…...
多层嵌套子查询
在优化多层嵌套子查询的 Hive SQL 时,除了常见的谓词下推、分区裁剪、WITH 子句复用和动态分区优化,还可以通过 抽象语法树(AST)分析 和 基于历史的优化(HBO) 进一步优化。以下是结合所有技术方向的完整方案…...
[架构之美]从零开始整合Spring Boot与Maven(十五)
[架构之美]从零开始整合Spring Boot与Maven(十五) 摘要:本文手把手教你通过Maven快速构建Spring Boot项目,涵盖项目初始化、自动配置、依赖管理及打包部署全流程,并附赠常见避坑指南。适合需要快速搭建企业级项目的开…...
第21天打卡
何时使用降维? 1.数据可视化 高维数据难以直接可视化(如超过3维),通过降维(如PCA、t-SNE、UMAP)投影到2D/3D空间,揭示数据分布、聚类或流形结构。 适用算法:t-SNE(非线…...
【小记】excel vlookup一对多匹配
一个学生报四门课,输出每个学生课程 应用概述操作预处理数据计数指令 COUNTIFS进行一对多匹配 vlookup 应用概述 应用场景:学生报名考试,需要整理成指定格式,发给考试院。 一个学生最多报考四门 格式实例:准考证号 …...
前端项目中单元测试与集成测试的管理实践
前端项目中单元测试与集成测试的管理实践 在现代前端工程化中,单元测试(Unit Test)和集成测试(Integration Test)已成为保障项目质量的重要手段。合理地组织和管理测试代码,不仅有助于持续集成,…...
亿级流量系统架构设计与实战(六)
微服务架构与网络调用 当某个业务从单体服务架构转变为微服务架构后,多个服务之间会通过网络调用形式形成错综复杂的依赖关系。 在微服务架构中 , 一个微服务正常工作依赖它与其他微服务之间的多级网络调用。 网络是脆弱的 , RPC 请求有较大的概率会遇到超时 、 抖动 、 断…...
记录踩过的坑-金蝶云苍穹平台-轻分析和轻报表(慢慢更新)
未发现AppIdName(qing rpt)服务或访问服务网络异常 前提是有许可和权限。 去console(云基础平台控制台),点击服务管理,编辑mservice-更新升级-环境变量,在appIds里增加qing_rpt 查看数据库 如果是采用公共数据源连接…...
Java并发编程,从线程安全到死锁避免的实战解析
Java并发编程是构建高性能系统的核心技能,但也伴随着复杂的挑战。本文通过实际代码示例,系统讲解线程安全、死锁、资源竞争等常见问题的解决方案,并深入探讨如何利用Java并发工具包(java.util.concurrent)构建健壮的并发程序。 一、线程安全问题与解决方案 1.1 共享资源的…...
基于Python的在线教育平台的设计与实现
标题:基于Python的在线教育平台的设计与实现 内容:1.摘要 随着互联网技术的飞速发展,在线教育平台成为了教育领域的重要组成部分。本研究的目的是设计并实现一个基于Python的在线教育平台,以满足用户多样化的学习需求。方法上,采用Python语言…...
多线程获取VI模块的YUV数据
一.RV1126 VI模块采集摄像头YUV数据的流程 step1:VI模块初始化 step2:启动VI模块工作 step3:开启多线程采集VI数据并保存 1.1初始化VI模块: VI模块的初始化实际上就是对VI_CHN_ATTR_S的参数进行设置、然后调用RK_MPI_VI_SetC…...
通过推测搜索加速大型语言模型推理 (SpecSearch) 论文总结
通过推测搜索加速大型语言模型推理 (SpecSearch) 论文总结 1. 研究背景与任务 基于树搜索的推理方法(如思维树Tree-of-Thoughts)通过探索多个中间推理步骤,显著增强了大型语言模型(LLMs)的推理能力。然而,这些方法需要生成大量推理思考,导致…...
从代码学习深度学习 - 语义分割和数据集 PyTorch版
文章目录 前言什么是语义分割?图像分割和实例分割Pascal VOC2012 语义分割数据集Pascal VOC2012 语义分割数据集介绍基本信息语义分割部分特点数据格式评价指标应用价值数据集获取使用提示辅助工具代码 (`utils_for_huitu.py`)读取数据预处理数据自定义语义分割数据集类读取数…...
【计算机视觉】OpenCV实战项目:Deep Machine Learning Tutors:基于OpenCV的实时面部识别系统深度解析
Deep Machine Learning Tutors:基于OpenCV的实时面部识别系统深度解析 1. 项目概述2. 技术原理2.1 面部识别流程2.2 关键技术组件2.2.1 Haar级联分类器2.2.2 深度特征提取 3. 项目实现细节3.1 系统架构3.2 核心算法实现3.2.1 人脸检测3.2.2 实时处理流水线 4. 项目运…...
OSCP - Proving Grounds - EvilBox-One
主要知识点 类似黑盒测试,毫无线索的情况下只能去猜,如果是php文件则会比较容易达到入侵的目的即使有php文件了,由于不知道代码,只能测试一下有没有文件包含漏洞所以反正没线索的时候,就只能猜 具体步骤 nmap扫描&a…...
初识Linux · 传输层协议TCP · 下
目录 前言: 滑动窗口和流量控制机制 流量控制 滑动窗口 1.滑动窗口如何移动 2.滑动窗口的大小如何变化的 3.如果发生了丢包如何解决(快重传) 拥塞控制 延迟应答 面向字节流 RST PSH URG 什么是 PSH? 什么是 URG&…...
OpenCv实战笔记(4)基于opencv实现ORB特征匹配检测
一、原理作用 ORB 原理(Oriented FAST and Rotated BRIEF): 特征点检测:使用 FAST 算法检测角点(关键点)。 方向计算:为每个关键点分配主方向,增强旋转不变性。 特征描述:…...
LeetCode LCR 007. 三数之和 (Java)
题目描述 给定一个整数数组 nums,判断是否存在三个元素 a, b, c,使得 a b c 0?找出所有满足条件且不重复的三元组。 解题思路 核心方法:排序 双指针 排序:首先将数组排序,便于后续去重和双指针操作。…...
Spark的三种部署模式及其特点与区别
Spark支持多种集群部署模式,主要分为以下三类: 部署模式特点适用场景资源管理依赖Local模式单机运行,所有进程(Driver、Executor)在同一个JVM中开发调试、小规模数据测试无集群资源管理,仅本地线程模拟无需…...
2505d,d的借用检查器
void func(scope ref int*) {}unique(int*) a ...; assert(a !is null);unique(int*) b a; assert(a is null); assert(b !is null);func(b); // ok用live作为检查器,不必有断定了. int* a ...; int* b a; // 所有权转至b *a 3; // 不能再用a.编译器保证约束指针. live…...
前端EXCEL插件,智表ZCELL产品V3.0 版本发布,底层采用canvas全部重构,功能大幅扩展,性能极致提升,满足千万级单元格加载
本次更新是底层全部重构,按照现代浏览器要求,采用canvas方式进行了重构,预留了将来扩展空间,特别是在大数据量性能提升方面有了较大提升,可以满足千万级单元格加载,欢迎大家体验使用。 体验地址࿱…...
如何理解编程中的递归、迭代与回归?
作为编程初学者,递归、迭代和回归这三个概念常常让人感到困惑。本文将通过生活化的比喻、Python代码示例和直观的对比,帮助你彻底理解这三个重要概念及其应用场景。 一、从生活比喻理解核心概念 1. 递归(Recursion)—— 俄罗斯套…...
【金仓数据库征文】学校AI数字人:从Sql Server到KingbaseES的数据库转型之路
摘要:本文围绕学校 AI 数字人项目从 Sql Server 数据库替换至 KingbaseES 数据库的实践展开,涵盖迁移背景、两种数据库对比、替换实施步骤、应用效果展示、问题与解决措施等多方面内容,为教育领域类似项目提供了详实参考。 目录 1.背景与需求…...
stm32 lcd绘制波形和频谱
一、项目准备 主要利用LCD驱动中的画点和画连线函数,驱动是正点原子给我写好了的画点和画线的函数等些相关函数 void LCD_Draw_Circle(u16 x0,u16 y0,u8 r); //画圆 void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2); //画线 二、画波形图函数实…...
深入理解卷积神经网络的输入层:数据的起点与预处理核心
内容摘要 本文围绕卷积神经网络输入层展开,详细介绍其在网络中的重要作用,包括接收不同领域数据的形式及传递数据的过程。深入解读数据预处理的关键操作,如去均值、归一化和PCA/白化。助力读者透彻理解输入层,为构建高效卷积神经…...
基于大模型与异步技术的股票分析系统实现
在金融量化分析领域,高效的数据获取与智能的策略决策是核心竞争力。本文结合异步数据抓取技术与大模型工具集成,构建一套完整的股票分析系统,实现从海量数据采集到智能信息查询的全流程自动化。 一、量化分析的数据基石:异步高效…...
BUCK基本原理学习总结-20250509
一、电感伏秒平衡特性 处于稳定状态的电感,开关导通时间(电流上升段)的伏秒数须与开关关断(电流下降段)时的伏秒数在数值上相等,尽管两者符号相反。这也表示,绘出电感电压对时间的曲线,导通时段曲线的面积必须等于关断时段曲线的面积。 二、BUCK的基本概念和原理 基…...