10.1Linux输入子系统介绍

输入设备介绍

鼠标、键盘、按键、触摸屏等提供输入支持的设备都属于输入设备,在Linux也提供了一套驱动框架“input 子系统”与之对应,用于抽象输入设备,并提供管理输入设备驱动和输入事件处理程序的功能

input 子系统

input 子系统用于管理各种输入设备的驱动程序和各种输入事件的事件处理程序,input 子系统分为3层:

  1. 输入设备驱动层:包含各种各样的输入设备驱动程序,用于驱动输入设备。
  2. input 核心层:承上起下,用于管理输入设备驱动程序和输入事件处理程序,并提供驱动层和事件处理层相互匹配、相互通信的功能。
  3. 输入事件处理层:包含各种各样的输入事件处理程序,用于对输入设备的硬件进行了抽象,以便应用层能更方便的和输入设备进行交互,其中 evdev 是一个通用输入事件处理程序,它能与所有输入设备进行匹配,此外对于鼠标、键盘、触摸屏等常用设备内核中也有相应的输入事件驱动程序。
    注意:输入设备和输入事件驱动是多对多的关系,即一个输入设备可能有多个输入事件处理程序,一个输入事件处理程序也可能有多个输入设备
    在这里插入图片描述
    在这里插入图片描述

struct input_dev 对象

input_dev 对象表示一个 input 设备,它的核心成员如下:

	//输入设备的名字,通过命令 cat /proc/bus/input/devices 可以查看const char *name;//在系统层次结构中设备的物理路径,通过命令 cat /proc/bus/input/devices 可以查看const char *phys;//设备的唯一识别码,通过命令 cat /proc/bus/input/devices 可以查看const char *uniq;//设备ID,包含总线类型、作者、产品和版本相关消息,它用于输入设备和事件处理程序间的匹配,此外它通过命令 cat /proc/bus/input/devices 可以查看struct input_id id;//设备属性位图,记录输入设备的一些属性unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];//输入设备支持的事件类型位图,表示输入设备可以支持哪些事件unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//表示相应事件可以上报的事件码位图//按键事件unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//相对坐标事件unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//绝对坐标事件unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//其他事件unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//led事件unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//声音事件unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//压力反馈事件unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//压力状态事件unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//触摸屏相关,是一个动态分配的数组,用于存储触摸点的状态struct input_mt *mt;//操作函数,一般由事件处理程序调用,用于控制输入设备,如 evdev 事件处理程序则是通过字符设备接口开放到应用层,供应用层进行调用int (*open)(struct input_dev *dev);void (*close)(struct input_dev *dev);int (*flush)(struct input_dev *dev, struct file *file);int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);//使用 input_handle 中的 h_node 成员构成的链表, Linux 通过 input_handle 对象关联输入设备和输入事件处理程序struct list_head h_list;

事件码、事件类型、设备属性定义参考 input_event_codes.h 文件,可以通过函数 void __set_bit (int nr, volatile void *addr) 直接设置位图中的某一位,也可以通过函数 void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) 函数完成事件码位图和事件类型位图的设置。
当设备支持重复事件后内核会为其开启一个定时器生成重复事件,无需设备驱动层重复上报。

注册和注销输入设备

注册输入设备:

  1. 分配输入设备;使用函数 struct input_dev *input_allocate_device(void)
  2. 初始化 input_dev 对象,主要是设置设备属性、支持的事件类型、对应的事件码
  3. 注册输入设备;使用函数 int input_register_device(struct input_dev *dev)
    注销输入设备:
  4. 注销输入设备;使用函数 void input_unregister_device(struct input_dev *dev)
  5. 释放输入设备;使用函数 void input_free_device(struct input_dev *dev)

struct input_handler 对象

input_handler 对象表示一个输入事件处理程序,其核心成员如下:

	//事件处理函数,用于处理输入设备上报的事件void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//事件处理函数,用于处理输入设备上报的事件,可处理多个事件,未定义时系统默认使用event实现void (*events)(struct input_handle *handle, const struct input_value *vals, unsigned int count);//事件过滤函数,用于处理输入设备上报的事件bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//自定义匹配函数,当系统匹配函数执行成功后才会调用此函数bool (*match)(struct input_handler *handler, struct input_dev *dev);//用于建立事件处理程序和输入设备的联系,通过调用 input_register_handle 实现int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);//用于断开事件处理程序和输入设备的联系,通过调用 input_unregister_handle 实现void (*disconnect)(struct input_handle *handle);//事件处理程序和输入设备建立联系后执行void (*start)(struct input_handle *handle);//名字const char *name;//设备匹配表,用于与输入设备匹配const struct input_device_id *id_table;//使用 input_handle 中的 d_node 成员构成的链表, Linux 通过 input_handle 对象关联输入设备和输入事件处理程序struct list_head h_list;

注册和注销输入事件处理程序

注册输入事件处理程序

  1. 初始化 input_handler 对象,主要包括各种事件处理函数、输入设备驱动关联函数、设备匹配表等
  2. 使用 int input_register_handler(struct input_handler *handler) 函数注册输入事件处理程序
    注销输入事件处理程序
  3. 使用 void input_unregister_handler(struct input_handler *handler) 注销输入事件处理程序

struct input_handle 对象

input_handle 用于关联输入设备和事件处理程序,其核心成员如下:

	//关联的输入设备struct input_dev *dev;//关联的事件处理程序struct input_handler *handler;//用于构建输入设备驱动程序中的 h_list 链表struct list_head	d_node;//用于构建输入事件处理程序中的 h_list 链表struct list_head	h_node;

建立或断开输入设备和输入事件处理程序的联系

建立输入设备驱动程序和输入事件处理程序的联系:

  1. 在注册输入设备驱动过的程中会遍历 input_handler_list 并调用 input_attach_handler 进行输入设备驱动程序和输入事件处理程序的匹配,同样在注册输入事件处理程序的过程中也会遍历 input_dev_list 并调用 input_attach_handler 进行输入设备驱动程序和输入事件处理程序的匹配(利用输入设备驱动程序的 id 和输入事件处理程序的 id_table 进行匹配)。
  2. 默认匹配函数执行成功后会调用输入事件处理程序提供的 match 函数
  3. 匹配完成后会调用事件处理程序提供 connect 函数
  4. 在 connect 函数中分配并初始化 input_handle 对象(这里需要使用 input_get_device 进行获取输入设备)
  5. 使用 input_register_handle 建立输入设备和输入事件处理程序的联系
    断开输入设备和输入事件处理程序的联系
  6. 当注销输入设备驱动程序或输入事件处理程序时会遍历其 h_list (管理的 input_handle 对象)链表,执行输入事件处理程序的 disconnect 函数
  7. 在 disconnect 函数中使用 input_unregister_handle 断开输入设备和输入事件处理程序的联系(这里还需要对输入设备进行 put_device 操作)

输入设备上报事件

通过函数 input_event 可以上报输入设备产生的输入事件:

	/*** dev 需要上报的 input_dev。* type 上报的事件类型,比如 EV_KEY。* code 事件码,如 KEY_0、 KEY_1 等等。* value 事件值,如按键事件 1 表示按键按下, 0 表示按键松开。*/void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value)

另外针对常见类型的事件还有专门的事件上报函数,这些函数都是通过 input_event 函数进行了二次封装实现,如 input_report_key 可用于上报按键事件

事件同步

输入设备事件上报完成后需要调用 input_sync 告诉内核此次上报结束:

	/*** 需要上报同步事件的 input_dev。*/void input_sync(struct input_dev *dev)

输入事件处理程序下发事件

输入事件处理程序可以通过 input_inject_event 下发事件给输入设备,它最终会调用到输入设备驱动的 event 函数,如 evdev 驱动在 connect 中注册了一个字符设备,并在字符设备的 write中调用了此函数,使得应用层可以向输入设备驱动程序发送事件。

	/*** handle 需要下发事件的 input_handle (在 input_handle 关联了事件处理程序和输入设备)* type 下发的事件类型* code 事件码* value 事件值*/void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)

在应用层通过evdev访问输入设备

evdev 是一个通用的输入事件处理程序,它能与所有输入设备进行匹配,在匹配成功后会创建一个字符设备,自然应用层也可以通过它创建的字符设备访问到对应的输入设备,其步骤如下:

  1. 包含头文件 #include <linux/input.h>。
  2. 打开设备(evdev 支持阻塞、非阻塞、 IO 多路复用、异步通知等机制, IO 多路复用、异步通知建议采用非阻塞方式打开,避免 IO 错误或连续读写时导致程序意外阻塞)。
  3. 通过 ioctl 获取设备信息,控制命令即参数参考内核中的 \drivers\input\evdev.c 中的 evdev_do_ioctl 函数。
  4. 通过 read 读取输入设备上报的事件,通过 write 发送事件给输入设备(如控制LED等)。
  5. 使用完成关闭输入设备。
    通过read函数读取到的是一些列的输入事件,其数据类型如下:
	struct timeval {__kernel_time_t tv_sec;__kernel_suseconds_ tv_usec;};struct input_event {//事件上报时间struct timeval time;//表示哪类事件,比如 EV_KEY 表示按键类、EV_REL 表示相对位移,EV_ABS 表示绝对位置__u16 type;//表示该类事件下的事件码,比如对于 EV_KEY 类事件它表示按键值,对于触摸屏它表示是 X 还是Y ,或是压力值__u16 code;//表示事件值,对于按键它表示按键状态,对于触摸屏,它表示坐标值 x 值或 y 值或压力值__s32 value;};

在读取据时,可以得到一个或多个数据(比如一个触摸屏的一个触点会上报 X 、Y 位置信息,还可能会上报压力值), 应用层可通过同步事件确定此次上报是否结束,对于同步事件其 type 、 code 、 value 三项都是 0

确定设备信息

通过命令 cat /proc/bus/input/devices 可以查看输入设备的详细信息,其内容格式如下:

	//设备 IDI: Bus=0019 Vendor=0000 Product=0001 Version=0000//设备名称N: Name="Power Button"//系统层次结构中设备的物理路径P: Phys=LNXPWRBN/button/input0//位于 sys 文件系统的路径S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0//设备的唯一标识码U: Uniq=//与输入设备关联的输入事件处理程序H: Handlers=kbd event0//设备属性B: PROP=0//设备支持的事件类型B: EV=3//可上报的按键位图B: KEY=10000000000000 0I: Bus=0011 Vendor=0001 Product=0001 Version=ab41N: Name="AT Translated Set 2 keyboard"P: Phys=isa0060/serio0/input0S: Sysfs=/devices/platform/i8042/serio0/input/input1U: Uniq=H: Handlers=sysrq kbd event1 ledsB: PROP=0B: EV=120013B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe//设备支持的其他事件B: MSC=10//设备上的指示灯B: LED=7......

使用命令读取数据

调试输入设备时,可使用 hexdump 读取输入设备上报的数据,其数据格式如下:

	|序号 | || |微秒   | |type| |code| |value  |0000340 0000 0000 0000 0000 508b   62dd   0000 00000000350 a54d 0009 0000 0000 0003   0000   9d3f 00000000360 508b 62dd 0000 0000 a54d   0009   0000 0000

GPIO按键输入驱动

原理图

硬件原理图如下所示,在引脚PG3上接了一个按键KEY0,按下按键时PG3为低电平,松开按键时PG3为高电平
在这里插入图片描述
在这里插入图片描述

设备树编写

设备树如下所示:

	intr_key {compatible = "intr_key";status = "okay";key-gpio = <&gpiog 3 GPIO_ACTIVE_LOW>;interrupt-parent = <&gpiog>;interrupts = <3 IRQ_TYPE_EDGE_BOTH>;};

驱动代码编写

驱动代码主要包括以下部分:

  1. 分配、初始化、注册输入设备
	//分配输入设备key->input_dev = devm_input_allocate_device(&pkey_dev->dev);if(!key->input_dev){printk("alloc input device failed");return -ENOMEM;}//设置输入设备名称key->input_dev->name = "virtual_input_Device";//设置事件类型,按键事件、重复事件__set_bit(EV_KEY, key->input_dev->evbit);__set_bit(EV_REP, key->input_dev->evbit);//设置事件码__set_bit(KEY_0, key->input_dev->keybit);//注册输入设备result = input_register_device(key->input_dev);if(result){printk("register input device failed");return result;}
  1. 延时消抖定时器注册,在按下按键时可能会产生抖动,导致误触发,我们可以等其稳定后在来读取按键值
	//初始化消抖定時器timer_setup(&key->timer, timer_func, 0);
  1. 设置GPIO为输入,获取并注册GPIO的中断处理函数
	//获取中断号key->irq = of_irq_get(pkey_dev->dev.of_node, 0);if(key->irq <= 0){input_unregister_device(key->input_dev);printk("irq get failed");return key->irq;}//获取中断触发方式irq_flags = irq_get_trigger_type(key->irq);if(irq_flags == IRQF_TRIGGER_NONE)irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;irq_flags |= IRQF_SHARED;//注册中断result = devm_request_irq(&pkey_dev->dev, key->irq, key_handler, irq_flags, "key", (void*)key);if(result != 0){input_unregister_device(key->input_dev);printk("request irq failed\r\n");return result;}
  1. 在中断函数中设置消抖定时器触发时间,然后再消抖定时器函数中上报按键状态
static irqreturn_t key_handler(int irq, void *dev)
{struct key_handle *key = (struct key_handle *)dev;//设置定时器到期时间mod_timer(&key->timer, jiffies + msecs_to_jiffies(10));//返回IRQ_HANDLED,表示中断被成功处理return IRQ_HANDLED;
}static void timer_func(struct timer_list *tm)
{struct key_handle *key = container_of(tm, struct key_handle, timer);//上报GPIO状态input_report_key(key->input_dev, KEY_0, gpiod_get_value(key->gpio));//上报完成input_sync(key->input_dev);
}

完整的按键输入驱动程序如下所示:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/input.h>struct key_handle {//GPIO描述符struct gpio_desc *gpio;//GPIO中断号unsigned int irq;//软件定时器,用于消抖struct timer_list timer;//输入设备struct input_dev *input_dev;
};static irqreturn_t key_handler(int irq, void *dev)
{struct key_handle *key = (struct key_handle *)dev;//设置定时器到期时间mod_timer(&key->timer, jiffies + msecs_to_jiffies(10));//返回IRQ_HANDLED,表示中断被成功处理return IRQ_HANDLED;
}static void timer_func(struct timer_list *tm)
{struct key_handle *key = container_of(tm, struct key_handle, timer);//上报GPIO状态input_report_key(key->input_dev, KEY_0, gpiod_get_value(key->gpio));//上报完成input_sync(key->input_dev);
}static int key_probe(struct platform_device *pkey_dev)
{int result;uint32_t irq_flags;struct key_handle *key;printk("%s\r\n", __FUNCTION__);//分配设备句柄,devm表示模块卸载时自动释放key = devm_kzalloc(&pkey_dev->dev, sizeof(struct key_handle), GFP_KERNEL);if(!key){printk("alloc memory failed\r\n");return -ENOMEM;}//复位key设备句柄memset(key, 0, sizeof(struct key_handle));//分配输入设备key->input_dev = devm_input_allocate_device(&pkey_dev->dev);if(!key->input_dev){printk("alloc input device failed");return -ENOMEM;}//设置输入设备名称key->input_dev->name = "virtual_input_Device";//设置事件类型,按键事件、重复事件__set_bit(EV_KEY, key->input_dev->evbit);__set_bit(EV_REP, key->input_dev->evbit);//设置事件码__set_bit(KEY_0, key->input_dev->keybit);//注册输入设备result = input_register_device(key->input_dev);if(result){printk("register input device failed");return result;}//初始化消抖定時器timer_setup(&key->timer, timer_func, 0);//获取GPIO,并设置为输入(devm表示模块卸载时自动释放)key->gpio = devm_gpiod_get_index(&pkey_dev->dev, "key", 0, GPIOD_IN);if(IS_ERR(key->gpio)){input_unregister_device(key->input_dev);printk("get gpio failed\r\n");return PTR_ERR(key->gpio);}//获取中断号key->irq = of_irq_get(pkey_dev->dev.of_node, 0);if(key->irq <= 0){input_unregister_device(key->input_dev);printk("irq get failed");return key->irq;}//获取中断触发方式irq_flags = irq_get_trigger_type(key->irq);if(irq_flags == IRQF_TRIGGER_NONE)irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;irq_flags |= IRQF_SHARED;//注册中断result = devm_request_irq(&pkey_dev->dev, key->irq, key_handler, irq_flags, "key", (void*)key);if(result != 0){input_unregister_device(key->input_dev);printk("request irq failed\r\n");return result;}//设置平台设备的驱动私有数据pkey_dev->dev.driver_data = (void*)key;return 0;
}static int key_remove(struct platform_device *pkey_dev)
{struct key_handle *key;printk("%s\r\n", __FUNCTION__);//提取平台设备的驱动私有数据key = (struct key_handle*)pkey_dev->dev.driver_data;//注销输入设备input_unregister_device(key->input_dev);return 0;
}/* 匹配列表,用于设备树和驱动进行匹配 */
static struct of_device_id key_of_match[] = {{.compatible = "intr_key"},{},
};static struct platform_driver key_drv = { .driver = {.name = "intr_key",.of_match_table = key_of_match,},.probe = key_probe,.remove = key_remove,
};static int __init mykey_init(void)
{return platform_driver_register(&key_drv);
}static void __exit mykey_exit(void)
{platform_driver_unregister(&key_drv);
}module_init(mykey_init);
module_exit(mykey_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("csdn");
MODULE_DESCRIPTION("key test");
MODULE_ALIAS("key_input");

驱动测试程序

按键驱动加载成功后会与event事件处理程序匹配,event事件处理程序会在/dev/input/目录中创建一个event*的设备文件,通过此设备文件可以读取到按键驱动上报的数据,其测试代码如下:

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h>
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdlib.h> 
#include <string.h> 
#include <linux/input.h>int main(int argc, char *argv[])
{int fd, ret;struct input_event ev;if(argc < 2){printf("Usage:\r\n\t./keyinputApp /dev/input/eventX @ Open Key\r\n");return -1;}//打开输入设备fd = open(argv[1], O_RDWR);if(0 > fd){printf("Error: file %s open failed!\r\n", argv[1]);return -1;}while(1){//读取输入设备ret = read(fd, &ev, sizeof(struct input_event));if (ret){//处理输入事件switch (ev.type){/* 按键事件 */case EV_KEY:if (KEY_0 == ev.code){/* 判断是不是 KEY_0 按键 */if (ev.value){/* 按键按下 */ printf("Key0 Press\n");}else{/* 按键松开 */ printf("Key0 Release\n");}}break;/* 重复事件 */case EV_REL:if (KEY_0 == ev.code){/* 按键一直按下 */printf("Key0 repetition\n");}break;/* 其他类型的事件,自行处理 */case EV_ABS:break;case EV_MSC:break;case EV_SW:break;};}else{printf("Error: file %s read failed!\r\n", argv[1]);break;}}/* 关闭设备 */close(fd);return 0;
}

上机测试

  1. 根据硬件原理图对设备树进行修改,然后用命令make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树,并用新的设备树启动目标板。
  2. 从这里下载代码并进行编译,然后将编译出来的.ko文件和.out文件拷贝到目标板根文件系统的root目录中。
  3. 执行命令insmod key.ko加载按键驱动,此时通过命令cat /proc/bus/input/devices可以看到按键的基本信息
    在这里插入图片描述
  4. 执行命令./app.out /dev/input/event0运行测试程序,测试程序会读取按键状态并输出,在按下或松开时终端都会输出相应字符串,长按时还会重复输出按下字符串。
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/225368.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPT 魔力涌现

GPT 二、Prompt 的典型构成 角色&#xff1a;给 AI 定义一个最匹配任务的角色&#xff0c;比如&#xff1a;「你是一位软件工程师」「你是一位小学老师」指示&#xff1a;对任务进行描述上下文&#xff1a;给出与任务相关的其它背景信息&#xff08;尤其在多轮交互中&#xff…

Java: Random

/*** encoding: utf-8* 版权所有 2023 涂聚文有限公司* 许可信息查看&#xff1a;* 描述&#xff1a; //https://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/RandomStringUtils.html* //https://commons.apache.org/pro…

详解RTC:以华人文化打造链上生态

文化是人类在发展的历史长河中淘洗出来的智慧结晶&#xff0c;随着人类社会的进步和变迁&#xff0c;经历了从口口相传到互联网等不同历史时代的传承和创新。在数字技术飞速发展的当今&#xff0c;区块链技术为文化的创新与传承提供了全新的空间和方式&#xff0c;使其得以在新…

ACL与NAT

目录 一、ACL &#xff08;一&#xff09;ACL基本理论 &#xff08;二&#xff09;ACL的类型 1.基本ACL 2.高级ACL 3.二层ACL &#xff08;三&#xff09;基本原理 &#xff08;四&#xff09;项目实验 通配符掩码 二、NAT &#xff08;一&#xff09;基本理论 &am…

ansible的基本使用

本章主要介绍在RHEL8中如何安装ansible 及 ansible 的基本使用。 ansible是如何工作的在 RHEL8中安装ansible编写ansible.cfg和清单文件ansible 的基本用法 如果管理的服务器很多&#xff0c;如几十台甚至几百台&#xff0c;那么就需要一个自动化管理工具了&#xff0c; ansi…

跟随鼠标动态显示线上点的值(基于Qt的开源绘图控件QCustomPlot进行二次开发)

本文为转载 原文链接&#xff1a; 采用Qt快速绘制多条曲线&#xff08;折线&#xff09;&#xff0c;跟随鼠标动态显示线上点的值&#xff08;基于Qt的开源绘图控件QCustomPlot进行二次开发&#xff09; 内容如下 QCustomPlot是一个开源的基于Qt的第三方绘图库&#xff0c;能…

打工人副业变现秘籍,某多/某手变现底层引擎-Stable Diffusion 黑白老照片上色修复

在这个时代,我们习惯于拥有高清、色彩丰富的照片,然而,那些古老的黑白色老照片由于年代的久远,往往会出现模糊、破损等现象。 那么今天要给大家介绍的是,用 Stable Diffusion 来修复老照片。 前段时间 ControlNet 的除了上线了“IP-Adapter”模型以外还增加另一个…

【深度学习】TensorFlow深度模型构建:训练一元线性回归模型

文章目录 1. 生成拟合数据集2. 构建线性回归模型数据流图3. 在Session中运行已构建的数据流图4. 输出拟合的线性回归模型5. TensorBoard神经网络数据流图可视化6. 完整代码 本文讲解&#xff1a; 以一元线性回归模型为例&#xff0c; 介绍如何使用TensorFlow 搭建模型 并通过会…

【Android12】Android Framework系列--AMS启动Activity分析

AMS启动Activity分析 通过ActivityManagerService(AMS)提供的方法&#xff0c;可以启动指定的Activity。比如Launcher中点击应用图标后&#xff0c;调用AMS的startActivity函数启动应用。 AMS提供的服务通过IActivityManager.aidl文件定义。 // frameworks/base/core/java/an…

Python将已标注的两张图片进行上下拼接并修改、合并其对应的Labelme标注文件(v2.0)

Python将已标注的两张图片进行上下拼接并修改、合并其对应的Labelme标注文件&#xff08;v2.0&#xff09; 前言前提条件相关介绍实验环境上下拼接图片并修改、合并其对应的Labelme标注文件代码实现输出结果 前言 此版代码&#xff0c;相较于Python将已标注的两张图片进行上下拼…

区块链的可扩展性研究【06】Plasma

1.Plasma&#xff1a;Plasma 是一种基于以太坊区块链的 Layer2 扩容方案&#xff0c;它通过建立一个分层结构的区块链网络&#xff0c;将大量的交易放到子链上进行处理&#xff0c;从而提高了以太坊的吞吐量。Plasma 还可以通过智能合约实现跨链交易&#xff0c;使得不同的区块…

【️Zookeeper是CP还是AP的?】

&#x1f60a;引言 &#x1f396;️本篇博文约3000字&#xff0c;阅读大约10分钟&#xff0c;亲爱的读者&#xff0c;如果本博文对您有帮助&#xff0c;欢迎点赞关注&#xff01;&#x1f60a;&#x1f60a;&#x1f60a; &#x1f5a5;️Zookeeper是CP还是AP的&#xff1f; ✅…

2024年20多个最有创意的AI人工智能点子

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 探索 2024 年将打造的 20 个基于人工智能产品的盈利创意 &#x1f525;&#x1f525;&#x1f525; 直到最近&#xff0c;企业对人工智能还不感兴趣&#xff0c;但…

浅析AI视频分析与视频管理系统EasyCVR平台及场景应用

人工智能的战略重要性导致对视频智能分析的需求不断增加。鉴于人工智能视觉技术的巨大潜力&#xff0c;人们的注意力正在从传统的视频监控转移到计算机视觉的监控过程自动化。 1、什么是视频分析&#xff1f; 视频分析或视频识别技术&#xff0c;是指从视频片段中提取有用信息…

Java设计模式-建造者模式

目录 一、需求 二、传统方法解决需求 三、基本介绍 四、注意事项和细节 一、需求 盖房项目需求 需要建房子&#xff1a;这一过程为打桩、砌墙、封顶 房子有各种各样的&#xff0c;比如普通房&#xff0c;高楼&#xff0c;别墅&#xff0c;各种房子的过程虽然一样&#xff…

RabbitMQ插件详解:rabbitmq_message_timestamp【Rabbitmq 五】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 RabbitMQ时空之旅&#xff1a;rabbitmq_message_timestamp的奇妙世界 前言什么是rabbitmq_message_timestamprabbitmq_message_timestamp 的定义与作用&#xff1a;如何在 RabbitMQ 中启用消息时间戳&…

【每次启动wsl时自动更新ip】

每次启动wsl时自动更新ip 在windows中使用wsl时&#xff0c;每次启动wsl后发现其ip都会改变&#xff0c;这样的话如果想通过vscode的Remote-SSH插件打开代码编辑器&#xff0c;就需要手动更新ssh配置文件&#xff0c;极为不便&#xff0c;所以考虑使用一种优雅的方式&#xff0…

abc组合 C语言xdoj54

问题描述 已知abccban&#xff0c;其中a,b,c均为一位数&#xff0c;1000<n<2000,编程求出满足条件的a,b,c所有组合。 输入说明 一个整数n 输出说明 按照整数abc从小到大的顺序,输出a, b, c, 用空格分隔&#xff0c;每输出一组a&#xff0c;b&#xff0c;c后换…