当前位置: 首页 > news >正文

# 基于OpenCV与Dlib的人脸融合技术实现

从仿射变换到人脸融合:基于OpenCV和Dlib的图像处理实践

在图像处理领域,仿射变换和人脸融合是两个非常有趣且实用的技术。仿射变换可以用于图像的几何变换,而人脸融合则可以创造出令人惊叹的视觉效果。本文将通过两个具体的代码示例,详细介绍如何使用Python、OpenCV和Dlib库实现这些功能。我们将从简单的仿射变换开始,逐步深入到复杂的人脸融合技术。

一、仿射变换:基础入门

(一)仿射变换简介

仿射变换是一种二维坐标到二维坐标的线性变换,它保持了图像的直线性和平行性。通过仿射变换,可以对图像进行平移、旋转、缩放和倾斜等操作。在计算机视觉中,仿射变换常用于图像校正、目标匹配等场景。

(二)代码实现

以下代码展示了如何使用OpenCV实现仿射变换:

import cv2
import numpy as np# 读取图像
img = cv2.imread("zjl.png")
height, width = img.shape[:2]# 定义源点和目标点
mat_src = np.float32([[0, 0], [0, height-1], [width-1, 0]])
mat_dst = np.float32([[0, 0], [100, height-100], [width-100, 100]])# 计算仿射变换矩阵
M = cv2.getAffineTransform(mat_src, mat_dst)# 应用仿射变换
dst = cv2.warpAffine(img, M, (width, height))# 显示结果
imgs = np.hstack([img, dst])
cv2.namedWindow('imgs', cv2.WINDOW_NORMAL)
cv2.imshow("imgs", imgs)
cv2.waitKey(0)
cv2.destroyAllWindows()

(三)代码解析

  1. 读取图像:使用cv2.imread函数读取图像文件。
  2. 定义源点和目标点mat_srcmat_dst分别定义了仿射变换的源点和目标点。这些点决定了图像如何被变换。
  3. 计算仿射变换矩阵:通过cv2.getAffineTransform函数计算源点和目标点之间的仿射变换矩阵。
  4. 应用仿射变换:使用cv2.warpAffine函数将仿射变换矩阵应用到图像上,生成变换后的图像。
  5. 显示结果:将原始图像和变换后的图像并排放置,使用cv2.imshow显示结果。

(四)运行结果

运行上述代码后,你将看到原始图像和经过仿射变换后的图像并排显示。仿射变换后的图像可能会出现平移、旋转或缩放的效果,具体取决于你定义的源点和目标点。
在这里插入图片描述

二、人脸融合:进阶应用

(一)人脸融合简介

人脸融合是一种将两张人脸图像的特征进行融合的技术,生成一张新的图像,同时保留两张人脸的主要特征。这种技术在娱乐、影视特效等领域有着广泛的应用。实现人脸融合的关键在于精确地检测人脸的关键点,并进行合理的图像变换和融合。

(二)代码实现

以下代码展示了如何使用OpenCV和Dlib实现人脸融合:

import cv2
import numpy as np
import dlib# 定义人脸关键点集合
JAW_POINTS = list(range(0, 17))
RIGHT_BROW_POINTS = list(range(17, 22))
LEFT_BROW_POINTS = list(range(22, 27))
NOSE_POINTS = list(range(27, 35))
RIGHT_EYE_POINTS = list(range(36, 42))
LEFT_EYE_POINTS = list(range(42, 48))
MOUTH_POINTS = list(range(48, 61))
FACE_POINTS = list(range(17, 68))
POINTS = [LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS]
POINTStupLe = tuple(POINTS)# 生成人脸掩膜
def getFacemask(im, keyPoints):im = np.zeros(im.shape[:2], dtype=np.float64)for p in POINTS:points = cv2.convexHull(keyPoints[p])cv2.fillConvexPoly(im, points, color=1)im = np.array([im, im, im]).transpose(1, 2, 0)im = cv2.GaussianBlur(im, (25, 25), 0)return im# 计算仿射变换矩阵
def getM(points1, points2):points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)points1 -= c1points2 -= c2s1 = np.std(points1)s2 = np.std(points2)points1 /= s1points2 /= s2U, S, Vt = np.linalg.svd(points1.T * points2)R = (U * Vt).Treturn np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))# 获取人脸关键点
def getKeyPoints(im):rects = detector(im, 1)shape = predictor(im, rects[0])s = np.matrix([[p.x, p.y] for p in shape.parts()])return s# 修改图像颜色
def normalColor(a, b):ksize = (111, 111)aGauss = cv2.GaussianBlur(a, ksize, 0)bGauss = cv2.GaussianBlur(b, ksize, 0)weight = aGauss / bGausswhere_are_inf = np.isinf(weight)weight[where_are_inf] = 0return b * weight# 加载图像和模型
a = cv2.imread("zjl2.png")
b = cv2.imread("zxc2.png")
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")# 获取关键点
aKeyPoints = getKeyPoints(a)
bKeyPoints = getKeyPoints(b)# 生成掩膜
aMask = getFacemask(a, aKeyPoints)
bMask = getFacemask(b, bKeyPoints)# 计算仿射变换矩阵并应用
M = getM(aKeyPoints[POINTStupLe], bKeyPoints[POINTStupLe])
dsize = a.shape[:2][::-1]
bMaskWarp = cv2.warpAffine(bMask, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)
bWarp = cv2.warpAffine(b, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)# 颜色校正和融合
mask = np.max([aMask, bMaskWarp], axis=0)
bcolor = normalColor(a, bWarp)
out = a * (1.0 - mask) + bcolor * mask# 显示结果
cv2.imshow("a", a)
cv2.imshow("b", b)
cv2.imshow("out", out / 255)
cv2.waitKey()
cv2.destroyAllWindows()

(三)代码解析

  1. 人脸关键点检测
    使用Dlib的人脸检测器和关键点检测器,从图像中提取人脸的关键点。这些关键点将用于后续的图像变换和融合。

    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
    
  2. 人脸掩膜生成
    定义getFacemask函数,生成人脸的掩膜。掩膜用于在融合过程中只处理人脸区域。

    def getFacemask(im, keyPoints):im = np.zeros(im.shape[:2], dtype=np.float64)for p in POINTS:points = cv2.convexHull(keyPoints[p])cv2.fillConvexPoly(im, points, color=1)im = np.array([im, im, im]).transpose(1, 2, 0)im = cv2.GaussianBlur(im, (25, 25), 0)return im
    
  3. 计算仿射变换矩阵
    定义getM函数,计算从一张人脸到另一张人脸的仿射变换矩阵。

def getM(points1, points2):points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)points1 -= c1points2 -= c2s1 = np.std(points1)s2 = np.std(points2)points1 /= s1points2 /= s2U, S, Vt = np.linalg.svd(points1.T * points2)R = (U * Vt).Treturn np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))
  1. 应用仿射变换
    使用OpenCV的cv2.warpAffine函数对人脸图像进行仿射变换。

    M = getM(aKeyPoints[POINTStupLe], bKeyPoints[POINTStupLe])
    dsize = a.shape[:2][::-1]
    bMaskWarp = cv2.warpAffine(bMask, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)
    bWarp = cv2.warpAffine(b, M, dsize, borderMode=cv2.BORDER_TRANSPARENT, flags=cv2.WARP_INVERSE_MAP)
    
  2. 颜色校正和融合
    定义normalColor函数,将一张图像的颜色调整为与另一张图像相似。然后将两张图像进行融合。

    def normalColor(a, b):ksize = (111, 111)aGauss = cv2.GaussianBlur(a, ksize, 0)bGauss = cv2.GaussianBlur(b, ksize, 0)weight = aGauss / bGausswhere_are_inf = np.isinf(weight)weight[where_are_inf] = 0return b * weight
    
  3. 显示结果
    使用OpenCV的cv2.imshow函数展示融合后的图像。

    cv2.imshow("a", a)
    cv2.imshow("b", b)
    cv2.imshow("out", out / 255)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

(四)运行结果

运行上述代码后,你将看到两张原始人脸图像和融合后的图像。融合后的图像将同时保留两张人脸的主要特征,看起来更加自然。
在这里插入图片描述

三、总结与展望

通过本文的介绍,我们从简单的仿射变换逐步深入到复杂的人脸融合技术。仿射变换是图像处理中的基础操作,而人脸融合则涉及到人脸检测、关键点提取、图像变换和融合等多个步骤。希望本文能够帮助读者更好地理解这些技术,并激发大家在实际项目中进一步探索的灵感。

未来,随着深度学习技术的发展,人脸融合技术将更加智能化和高效化。例如,可以使用深度学习模型来生成更加自然和逼真的融合效果,或者实现实时的人脸融合功能。此外,还可以将人脸融合技术应用于更多领域,如虚拟现实、增强现实等,为用户带来更加丰富的视觉体验。

相关文章:

# 基于OpenCV与Dlib的人脸融合技术实现

从仿射变换到人脸融合:基于OpenCV和Dlib的图像处理实践 在图像处理领域,仿射变换和人脸融合是两个非常有趣且实用的技术。仿射变换可以用于图像的几何变换,而人脸融合则可以创造出令人惊叹的视觉效果。本文将通过两个具体的代码示例&#xf…...

多光谱相机:水环境监测(水体富营养化、黑臭水体、藻类水华)

随着全球水体污染问题日益严峻,水体富营养化、黑臭水体和藻类水华等生态危机对人类健康和水生系统构成重大威胁。传统监测手段(如人工采样、单点传感器)因效率低、覆盖不足、实时性差等局限,难以满足复杂水环境的动态监管需求。多…...

记录一次nginx访问前端首页,一直显示nginx首页问题(实际是nginx访问页面权限问题)

同一台服务器,nginx配置是server { listen 8081; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location /New_mh_other { alias /home/hqu/data/new_mh_other; try…...

windows下命名管道双端通信

实现功能 1、命名管道双端通信&#xff08;异步&#xff09; 2、客户端断线重连 PS&#xff1a;多线程版本 PipeWrapper.h #include <windows.h> #include <string> #include <vector> #include "Utils/ThreadObject.h" #include "Utils/T…...

Linux自行实现的一个Shell(15)

文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…...

powerDesign 逆向 mysql 生成 物理模型,并用VBS脚本整理comment

学习自&#xff1a;https://www.cnblogs.com/xmyjcs/p/8536233.html 文章目录 Reverse Engineer格式化模型执行 VBS 脚本 Reverse Engineer 下面 DBMS 可以通过 ODBC&#xff08;Open Database Connectivity&#xff0c;开放数据库连接&#xff09;连接&#xff0c; 需要自己先…...

跨境全域中台:前端独立站群+后端共享云仓的协同作战体系

在全球化浪潮与互联网技术飞速发展的当下&#xff0c;跨境电商已然成为国际贸易领域中最为活跃的力量。据相关数据显示&#xff0c;过去几年跨境电商的年增长率持续保持高位&#xff0c;越来越多的企业投身于这片充满机遇与挑战的蓝海市场。在竞争日益激烈的跨境电商赛道上&…...

国产芯片解析:乐得瑞LDR6500C 超小封装全能芯片,赋能智能设备未来

LDR6500C是乐得瑞科技针对USB-C标准中的Bridge设备而开发的双USB-C DRP接口PD通信芯片&#xff0c;具备切换Data Role功能&#xff0c;支持最高USB PD 100W 充电&#xff0c;并且针对各大品牌设备的 USB-C 兼容性进行了特别优化&#xff0c;非常适合于 USB Type-C 设备快充转接…...

代码随想录-06-二叉树-05.10 二叉树的最小深度

二叉树的最小深度 #模板题 题目描述 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明叶子节点是指没有子节点的节点 具体思路&#xff08;暴力&#xff09; 层序遍历;找到cur.left null && cur.ri…...

系统与网络安全------网络通信原理(6)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 应用层解析 DNS Domain Name System&#xff0c;域名系统 用来完成域名与IP地址之间的映射&#xff0c;便于用户对网站的记忆和访问 端口号为TCP或UDP的53 DNS工作原理 FTP File Transfer Protocol 文件…...

【Vue #2】脚手架 指令

一、脚手架 脚手架&#xff1a;一个保证各项工作顺利开展的平台&#xff0c;方便我们 拿来就用&#xff0c;零配置 1. Vue 代码开发方式 相比直接 script 引入 vue 源码&#xff0c;有没有更好的方式编写vue代码呢? ① 传统开发模式&#xff1a; 基于html文件开发Vue&…...

UE5 后坐力枪口上抬和恢复

文章目录 计算后坐力并让视角上抬后坐力回落 计算后坐力并让视角上抬 在玩家蓝图里&#xff0c;声明一个方法OnShootOnce,在武器每次射击时调用 1检测新的后坐力是否超过了最大后坐力&#xff0c;并选择一个小的 2 如何将角色模型设置为相机的子物体 3 最后记录一下当前的…...

Mysql B+树高度如何计算?

MySQL 的 InnoDB 存储引擎使用 B+树 作为索引结构,其高度增加会直接影响查询性能(每次高度增加意味着多一次磁盘 I/O)。以下是 B+树高度增加的 关键场景 和 优化建议: 1. B+树高度增加的触发条件 (1) 数据量持续增长 根本原因:B+树的层级由数据量(记录数)和每个节点的容…...

UE5 使用贴花创建弹孔

文章目录 使用射线检测击中点在击中点处创建贴花 使用射线检测击中点 和untiy一样&#xff0c;发射一条射线&#xff0c;在命中点处创建弹孔 在武器里定义射击检测方法 以下是对上边使用的方法的展开 GetShootStartPosition:获取射击起点 computeShootEndPosition&#xff1a…...

程序持续内存泄漏问题定位参考

0 概括 本文用于记录 x86-Linux 应用程序发生持续性内存泄漏问题时的定位方法。主要介绍valgrind工具的应用。 1 原理 对于内存泄漏问题的定位&#xff0c;一种朴素的想法就是对内存申请点进行监控。对于一个内存申请调用点&#xff08;例如c/c中的malloc函数&#xff09;&a…...

Lumion 与 Enscape 怎么选?附川翔云电脑适配指南

建筑可视化领域&#xff0c;Lumion 和 Enscape 是两款主流实时渲染器&#xff0c;核心差异体现在操作逻辑、渲染特性及适用场景。结合川翔云电脑平台的硬件支持&#xff0c;可进一步优化使用体验。 一、核心差异&#xff1a;效率、操作与场景适配 1. 操作门槛与实时性 Lumio…...

WebShell详解:原理、分类、攻击与防御

目录 一、WebShell的定义与核心概念 二、WebShell的分类 三、WebShell的攻击原理与常见手法 1. 攻击原理 2. 常见攻击路径 四、WebShell的危害 五、防御与检测策略 六、总结 一、WebShell的定义与核心概念 ​​WebShell​​是一种以ASP、PHP、JSP等网页脚本形式存在的恶…...

Ubuntu 24.04 中文输入法安装

搜狗输入法&#xff0c;在Ubuntu 24.04上使用失败&#xff0c;安装教程如下 https://shurufa.sogou.com/linux/guide 出现问题的情况&#xff0c;是这个帖子里描述的&#xff1a; https://forum.ubuntu.org.cn/viewtopic.php?t493893 后面通过google拼音输入法解决了&#x…...

数码视讯TR100系列/TR100-G1/TR100-G4/数码视讯F7-国科GK6323V100C芯片-刷机固件包

数码视讯TR100系列&#xff0f;数码视讯TR100-G1&#xff0f;数码视讯TR100-G4&#xff0f;数码视讯F7-国科GK6323V100C芯片-刷机固件包 刷机教程&#xff1a; 里面共有两种方法&#xff0c;一是TTL线刷烧录方法&#xff1b;二是卡刷固件包&#xff1b; 下面以数码视讯TR100-…...

Cloudflare教程:免费优化CDN加速配置,提升网站访问速度 | 域名访问缓存压缩视频图片媒体文件优化配置

1、启用 Tiered Cache 缓存开关&#xff1a;通过选择缓存拓扑&#xff0c;可以控制源服务器与 Cloudflare 数据中心的连接方式&#xff0c;以确保缓存命中率更高、源服务器连接数更少&#xff0c;并且 Internet 延迟更短。 2、增加浏览器缓存时间TTL&#xff1a;在此期间&#…...

24体育NBA足球直播M24模板自适应板源码

源码名称&#xff1a;体育直播赛事扁平自适应M24直播模板源码 开发环境&#xff1a;帝国cms7.5 空间支持&#xff1a;phpmysql 带软件采集&#xff0c;可以挂着自动采集发布&#xff0c;无需人工操作&#xff01; 演示地址&#xff1a;https://www.52muban.com/shop/184022.h…...

dify+wan2.1搭建文生视频生成工具流

本文介绍在dify中使用阿里开源的Wan2.1 1.3B模型搭建文生视频工作流的方法。 使用的工具如下: 1、dify(官方:https://docs.dify.ai/zh-hans) 2、comfyui 一、comfyui安装 为了简单起见,本文介绍使用autodl完成comfyui的部署。在autodl创建实例,使用的镜像如下图: 大…...

C# js 判断table中tr否存在相同的值

html 中如&#xff1a; 实现&#xff1a;table数据表格中&#xff0c;点击删除按钮时&#xff0c;验证相同子订单号条数是否大于1&#xff0c;大于允许删除。保证数据表格中只有唯一的一条子订单号数据。 <table style"width: 100%; background-color: #fff;" ce…...

HTML5+CSS3小实例:纯CSS绘制七巧板

实例:纯CSS绘制七巧板 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale…...

什么是虚拟线程?与普通线程的区别

引言&#xff1a;线程的演进与挑战 在传统的并发编程中&#xff0c;线程是一种非常重要的概念。我们使用线程来实现任务的并发执行&#xff0c;从而提高程序的执行效率。普通线程&#xff08;如 Thread 类&#xff09;是一种重量级的线程&#xff0c;每个线程都对应着操作系统…...

Docker MySQL的主从同步 数据备份 数据同步 配置文件

创建主库 docker run \--namemysql_1 \-e MYSQL_ROOT_PASSWORD123456 \-p 3306:3306 \-v mysql_main_data:/var/lib/mysql \--restart unless-stopped \-d \mysql:8.0进入容器内部 docker exec -it mysql_1 bash查找配置文件 find / -name my.cnf复制出主机 docker cp mysql…...

PowerBI中的DATEDIFF函数

一、语法 DATEDIFF(开始日期&#xff0c;结束日期&#xff0c;日期时间类型)&#xff0c;返回两个日期之间的时间差 二、示例 开始日期结束日期度量值计算结果2025/4/1 15:33:202025/4/1 15:33:30计算 DATEDIFF([开始日期],[结束日期],SECOND)102025/4/1 15:332025/4/1 15:3…...

C/C++共有的类型转换与c++特有的四种强制类型转换

前言 C 语言和 C 共有的类型转换&#xff1a; 自动类型转换&#xff08;隐式类型转换&#xff09;&#xff1a; 编译器在某些情况下会自动进行的类型转换。强制类型转换&#xff08;显示类型转换&#xff09;&#xff1a; 使用 (type)expression 或 type(expression) 语法进行…...

体验OceanBase的 并行导入功能

在数据库的日常使用中&#xff0c;会经常遇到以下场景&#xff1a; ‌数据复制‌&#xff1a;将一个或多个表中的数据复制到目标表中&#xff0c;可能是复制全部数据&#xff0c;也可能仅复制部分数据。数据合并&#xff1a;将数据从一个表转移到另一个表&#xff0c;或者将多…...

CSS的字体

在 CSS 中&#xff0c;字体&#xff08;font&#xff09;是网页设计中的一个重要部分&#xff0c;它控制了文本的外观和排版效果。通过设置不同的字体属性&#xff0c;我们可以使网页上的文字更具吸引力和可读性。以下是与字体相关的 CSS 属性及其用法&#xff1a; 1️⃣ font…...

开源模型应用落地-LangChain与MCP协议-集成GPT-4o构建下一代AI智能体的全栈实践(三)

一、前言 在人工智能技术快速迭代的今天,大型语言模型(LLM)如何高效集成外部工具与多模态能力,成为开发者面临的核心挑战。Anthropic推出的模型上下文协议(MCP)​通过标准化工具接口,为AI应用提供了“即插即用”的生态基础,而LangChain凭借其模块化设计,正成为连接LLM…...

Qt 5.14.2入门(一)写个Hello Qt!程序

目录 参考链接&#xff1a;一、新建项目二、直接运行三、修改代码增加窗口内容1、Qt 显示一个 QLabel 标签控件窗口2、添加按键 参考链接&#xff1a; Qt5教程&#xff08;一&#xff09;&#xff1a;Hello World 程序 Qt 编程指南 一、新建项目 1、新建一个项目&#xff08…...

FPGA_DDR(二)

在下板的时候遇到问题 1&#xff1a;在写一包数据后再读&#xff0c;再写再读 这时候读无法读出 查看时axi_arready没有拉高 原因 &#xff1a; 由于读地址后没有拉高rready,导致数据没有读出卡死现象。 解决结果...

思科交换机配置

以下是交换机配置的详细步骤指南&#xff0c;适用于Cisco交换机&#xff0c;其他品牌需调整命令&#xff1a; 1. 初始连接与基本配置 连接方式&#xff1a;使用Console线连接交换机&#xff0c;通过终端软件&#xff08;如PuTTY&#xff09;登录。波特率&#xff1a;9600&…...

单链表——C语言实现

目录 一.相关指针知识点 二.链表 1.为什么学了顺序表还要学链表 2.优点 三.实现 1.链表的打印 —— 理解链表结构 (2) 物理结构图 2.链表的尾插 —— 入门 错误写法&#xff1a;tail ! NULL 总结&#xff1a; 正确代码物理图解&#xff1a; (2) 尾插整体代码 (思考…...

PostgreSQL插件生态全景解析:赋能数据库的无限可能

PostgreSQL以其开放的扩展生态闻名于世&#xff0c;其插件机制如同瑞士军刀般灵活&#xff0c;能够在不修改核心代码的前提下实现功能无限延伸。本文将基于多年内核开发经验&#xff0c;深度剖析PostgreSQL插件生态体系&#xff0c;为架构师与开发者提供全景式技术选型参考。 一…...

minio提供nfs服务

minio提供nfs服务 挂载minio为本地目录开机自动挂载使用supervisor实现开机自动挂载服务单元实现开机自动挂载minio为本地目录---失败 调试 挂载minio为本地目录 使用 Minio 作为后端存储&#xff0c;并通过 NFS 为客户端提供访问&#xff0c;那么你需要一个中间层来将 Minio …...

QML中的信号与槽机制

QML 中的信号与槽机制是 Qt 框架的核心特性之一&#xff0c;它提供了一种对象间通信的强大方式。与 C 中的信号槽类似&#xff0c;但语法更加简洁。 基本概念 1. 信号 (Signal) 当某个特定事件发生时由对象自动发出的通知 例如&#xff1a;按钮被点击时发出的 clicked 信号 …...

使用 Ktor 构建现代 Android 应用的后端服务

使用 Ktor 构建现代 Android 应用的后端服务 前言 在移动互联网时代&#xff0c;Android 应用对后端服务的实时性与性能要求越来越高&#xff0c;而传统的后端框架在一些场景中存在复杂度高、扩展性不足等问题。Ktor 作为 JetBrains 推出的异步 Web 框架&#xff0c;充分利用…...

leetcode_454. 四数相加 II_java

454. 四数相加 IIhttps://leetcode.cn/problems/4sum-ii/ 1、题目 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] …...

类名与协议名相同,开发中应该避免吗?

在 Objective-C 开发中&#xff0c;协议与实现类之间的命名关系非常重要。虽然语言允许协议名和类名相同&#xff0c;但从可读性和维护性等角度出发&#xff0c;这种做法并不推荐。本文通过一个典型示例展开分析&#xff0c;并提供更合理的命名建议。 一、示例 在某项目中&…...

环信鸿蒙版 UIKit 快速上手指南

环信鸿蒙版 UIKit 是专为 HarmonyOS 开发者设计的 IM UI 组件库&#xff0c;基于环信 IM SDK 开发&#xff0c;可帮助开发者快速集成即时通讯功能。 环信UIKit 的特点 ArkUI 声明式开发范式&#xff1a;采用高效简洁的声明式开发方式状态管理 V2&#xff1a;支持深度观测和精…...

编译freecad

git clone --recurse-submodules https://github.com/FreeCAD/FreeCAD.git freecad-source 手动安装 vscode 扩展安装cmake tool cmake ../ 缺什么装什么 Third Party Libraries - FreeCAD Documentation sudo apt install qt6-base-dev sudo apt install libyaml-cpp-dev …...

安卓Kotlin接入高德定位和地图SDK

前言&#xff1a;高德的定位sdk可以获取设备当前的详细信息&#xff0c;如经纬度&#xff0c;具体地址&#xff08;省->街道&#xff09;等&#xff0c; 本文主要使用的是定位sdk和地图sdk中的poi搜索功能&#xff08;以当前位置半径多少米内的关键词搜索&#xff09; 目录…...

JavaScript浅拷贝与深拷贝

目录 浅拷贝&#xff08;Shallow Copy&#xff09; 一、浅拷贝的定义 二、直接赋值 vs 浅拷贝 1. 直接赋值 2. 浅拷贝 三、数组的浅拷贝方法 1. slice() 2. concat() 3. 扩展运算符&#xff08;...&#xff09; 四、对象的浅拷贝方法 1. Object.assign() 2. 扩展运…...

智能生态之城-广东茂名

故事摘要 在中国广东茂名的未来社区&#xff0c;晨光中&#xff0c;垂直果园里发光的荔枝与智能无人机的早餐派送唤醒了城市的生活。在海底透明隧道的图书馆里&#xff0c;孩子们通过声控设备与虚拟生物互动。面对暴雨来临时&#xff0c;市民们积极参与到荔枝蜜饯制作和雨季造林…...

【Android】Android Activity 横屏设置详解及常见异常问题解决方法汇总

在 Android 开发中&#xff0c;我们经常需要控制 Activity 的屏幕方向&#xff0c;例如视频播放、游戏、VR/AR 应用等场景通常希望默认横屏显示。本文将讲解如何通过 Manifest 配置 和 Java/Kotlin 代码 设置横屏显示&#xff0c;并分析常见设置无效的原因与解决方法。 一、通过…...

Android 存储路径

​​一、内部存储路径&#xff08;Internal Storage&#xff09;​​ ​​stats.codeSize&#xff08;内部代码大小&#xff09;​​ ​​路径​​&#xff1a;/data/app/com.example.test-{随机后缀}/base.apk ​​说明​​&#xff1a;APK 安装路径&#xff0c;包含应用代码…...

【12】数据结构之基于线性表的排序算法

目录标题 插入排序直接插入排序折半插入排序希尔排序 交换排序冒泡排序快速排序 归并排序时间复杂度对比最好情况平均情况最坏情况 空间复杂度对比 插入排序 基本思想&#xff1a;将一个元素插入到一个有序序列中&#xff0c;继而得到一个有序的元素个数加一的新序列. 直接插…...

解决RecyclerView在调用smoothScrollToPosition后最后一个item底部超出屏幕的问题

要解决RecyclerView在调用smoothScrollToPosition后最后一个item底部超出屏幕的问题&#xff0c;可以使用自定义的LinearSmoothScroller&#xff0c;使其底部对齐屏幕。步骤如下&#xff1a; 创建自定义的SmoothScroller类&#xff1a; 继承LinearSmoothScroller并重写getVerti…...