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

面部表情识别3:Android实现表情识别(含源码,可实时检测)

面部表情识别3:Android实现表情识别(含源码,可实时检测)

目录

面部表情识别3:Android实现表情识别(含源码,可实时检测)

1.面部表情识别方法

2.人脸检测方法

3.面部表情识别模型训练

4.面部表情识别模型Android部署

(1) 将Pytorch模型转换ONNX模型

(2) 将ONNX模型转换为TNN模型

(3) Android端上部署模型

(4) Android测试效果 

(5) 运行APP闪退:dlopen failed: library "libomp.so" not found

5.项目源码下载


这是项目《面部表情识别》系列之《Android实现表情识别(含源码,可实时检测)》,主要分享将Python训练后的面部表情识别模型移植到Android平台。我们将开发一个简易的、可实时运行的面部表情识别的Android Demo。准确率还挺高的,采用轻量级mobilenet_v2模型的面部表情识别准确率也可以高达94.72%左右,基本满足业务性能需求。

项目将手把手教你将训练好的表情识别模型部署到Android平台中,包括如何转为ONNX,TNN模型,并移植到Android上进行部署,实现一个表情识别的Android Demo APP 。APP在普通Android手机上可以达到实时的检测识别效果,CPU(4线程)约30ms左右,GPU约25ms左右 ,基本满足业务的性能需求。

尊重原创,转载请注明出处】https://blog.csdn.net/guyuealian/article/details/129467015

先展示一下Android版本表情识别Demo效果: 

  

Android面部表情识别APP Demo体验:https://download.csdn.net/download/guyuealian/87575425

或者链接: https://pan.baidu.com/s/16OOi-qCENP4WbIeSzO5e9g 提取码: cs5g 


更多项目《面部表情识别》系列文章请参考:

  1. 面部表情识别1:表情识别数据集(含下载链接):
  2. 面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)
  3. 面部表情识别3:Android实现表情识别(含源码,可实时检测)
  4. 面部表情识别4:C++实现表情识别(含源码,可实时检测)


1.面部表情识别方法

面部表情识别方法有多种实现方案,这里采用最常规的方法:基于人脸检测+面部表情分类识别方法,即先采用通用的人脸检测模型,进行人脸检测,然后裁剪人脸区域,再训练一个面部表情分类器,完成对面部表情识别;

这样做的好处,是可以利用现有的人脸检测模型,而无需重新训练人脸检测模型,可减少人工标注成本低;而人脸数据相对而言比较容易采集,分类模型可针对性进行优化。


2.人脸检测方法

本项目人脸检测训练代码请参考:https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB 

这是一个基于SSD改进且轻量化后人脸检测模型,很slim,整个模型仅仅1.7M左右,在普通Android手机都可以实时检测。人脸检测方法在网上有一大堆现成的方法可以使用,完全可以不局限我这个方法。

​​

关于人脸检测的方法,可以参考我的另一篇博客:

行人检测和人脸检测和人脸关键点检测(C++/Android源码)


3.面部表情识别模型训练

关于面部表情识别模型的训练方法,请参考本人另一篇博文《面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)》:https://blog.csdn.net/guyuealian/article/details/129505205


4.面部表情识别模型Android部署

目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行Android端上部署。部署流程可分为四步:训练模型->将模型转换ONNX模型->将ONNX模型转换为TNN模型->Android端上部署TNN模型。

(1) 将Pytorch模型转换ONNX模型

训练好Pytorch模型后,我们需要先将模型转换为ONNX模型,以便后续模型部署。

  • 原始项目提供转换脚本,你只需要修改model_file为你模型路径即可
  •  convert_torch_to_onnx.py实现将Pytorch模型转换ONNX模型的脚本
python libs/convert/convert_torch_to_onnx.py
"""
This code is used to convert the pytorch model into an onnx format model.
"""
import sys
import ossys.path.insert(0, os.getcwd())
import torch.onnx
import onnx
from classifier.models.build_models import get_models
from basetrainer.utils import torch_toolsdef build_net(model_file, net_type, input_size, num_classes, width_mult=1.0):""":param model_file: 模型文件:param net_type: 模型名称:param input_size: 模型输入大小:param num_classes: 类别数:param width_mult::return:"""model = get_models(net_type, input_size, num_classes, width_mult=width_mult, is_train=False, pretrained=False)state_dict = torch_tools.load_state_dict(model_file)model.load_state_dict(state_dict)return modeldef convert2onnx(model_file, net_type, input_size, num_classes, width_mult=1.0, device="cpu", onnx_type="default"):model = build_net(model_file, net_type, input_size, num_classes, width_mult=width_mult)model = model.to(device)model.eval()model_name = os.path.basename(model_file)[:-len(".pth")] + ".onnx"onnx_path = os.path.join(os.path.dirname(model_file), model_name)# dummy_input = torch.randn(1, 3, 240, 320).to("cuda")dummy_input = torch.randn(1, 3, input_size[1], input_size[0]).to(device)# torch.onnx.export(model, dummy_input, onnx_path, verbose=False,#                   input_names=['input'],output_names=['scores', 'boxes'])do_constant_folding = Trueif onnx_type == "default":torch.onnx.export(model, dummy_input, onnx_path, verbose=False, export_params=True,do_constant_folding=do_constant_folding,input_names=['input'],output_names=['output'])elif onnx_type == "det":torch.onnx.export(model,dummy_input,onnx_path,do_constant_folding=do_constant_folding,export_params=True,verbose=False,input_names=['input'],output_names=['scores', 'boxes', 'ldmks'])elif onnx_type == "kp":torch.onnx.export(model,dummy_input,onnx_path,do_constant_folding=do_constant_folding,export_params=True,verbose=False,input_names=['input'],output_names=['output'])onnx_model = onnx.load(onnx_path)onnx.checker.check_model(onnx_model)print(onnx_path)if __name__ == "__main__":net_type = "mobilenet_v2"width_mult = 1.0input_size = [128, 128]num_classes = 2model_file = "work_space/mobilenet_v2_1.0_CrossEntropyLoss/model/best_model_022_98.1848.pth"convert2onnx(model_file, net_type, input_size, num_classes, width_mult=width_mult)

(2) 将ONNX模型转换为TNN模型

目前CNN模型有多种部署方式,可以采用TNN,MNN,NCNN,以及TensorRT等部署工具,鄙人采用TNN进行Android端上部署

TNN转换工具:

  • (1)将ONNX模型转换为TNN模型,请参考TNN官方说明:TNN/onnx2tnn.md at master · Tencent/TNN · GitHub
  • (2)一键转换,懒人必备:一键转换 Caffe, ONNX, TensorFlow 到 NCNN, MNN, Tengine   (可能存在版本问题,这个工具转换的TNN模型可能不兼容,建议还是自己build源码进行转换,2022年9约25日测试可用)

​​

(3) Android端上部署模型

项目实现了Android版本的面部表情识别Demo,部署框架采用TNN,支持多线程CPU和GPU加速推理,在普通手机上可以实时处理。项目Android源码,核心算法均采用C++实现,上层通过JNI接口调用.

如果你想在这个Android Demo部署你自己训练的分类模型,你可将训练好的Pytorch模型转换ONNX ,再转换成TNN模型,然后把TNN模型代替你模型即可。

  • 这是项目Android源码JNI接口 ,Java部分
package com.cv.tnn.model;import android.graphics.Bitmap;public class Detector {static {System.loadLibrary("tnn_wrapper");}/**** 初始化检测模型* @param det_model: 检测模型(不含后缀名)* @param cls_model: 识别模型(不含后缀名)* @param root:模型文件的根目录,放在assets文件夹下* @param model_type:模型类型* @param num_thread:开启线程数* @param useGPU:是否开启GPU进行加速*/public static native void init(String det_model, String cls_model, String root, int model_type, int num_thread, boolean useGPU);/**** 返回检测和识别结果* @param bitmap 图像(bitmap),ARGB_8888格式* @param score_thresh:置信度阈值* @param iou_thresh:  IOU阈值* @return*/public static native FrameInfo[] detect(Bitmap bitmap, float score_thresh, float iou_thresh);
}
  • 这是Android项目源码JNI接口 ,C++部分
#include <jni.h>
#include <string>
#include <fstream>
#include "src/object_detection.h"
#include "src/classification.h"
#include "src/Types.h"
#include "debug.h"
#include "android_utils.h"
#include "opencv2/opencv.hpp"
#include "file_utils.h"using namespace dl;
using namespace vision;static ObjectDetection *detector = nullptr;
static Classification *classifier = nullptr;JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {return JNI_VERSION_1_6;
}JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {}extern "C"
JNIEXPORT void JNICALL
Java_com_cv_tnn_model_Detector_init(JNIEnv *env,jclass clazz,jstring det_model,jstring cls_model,jstring root,jint model_type,jint num_thread,jboolean use_gpu) {if (detector != nullptr) {delete detector;detector = nullptr;}std::string parent = env->GetStringUTFChars(root, 0);std::string det_model_ = env->GetStringUTFChars(det_model, 0);std::string cls_model_ = env->GetStringUTFChars(cls_model, 0);string det_model_file = path_joint(parent, det_model_ + ".tnnmodel");string det_proto_file = path_joint(parent, det_model_ + ".tnnproto");string cls_model_file = path_joint(parent, cls_model_ + ".tnnmodel");string cls_proto_file = path_joint(parent, cls_model_ + ".tnnproto");DeviceType device = use_gpu ? GPU : CPU;LOGW("parent     : %s", parent.c_str());LOGW("useGPU     : %d", use_gpu);LOGW("device_type: %d", device);LOGW("model_type : %d", model_type);LOGW("num_thread : %d", num_thread);ObjectDetectionParam model_param = FACE_MODEL;detector = new ObjectDetection(det_model_file,det_proto_file,model_param,num_thread,device);//ClassificationParam ClassParam = FACE_MASK_MODEL;ClassificationParam ClassParam = EYEGLASSES_MODEL;classifier = new Classification(cls_model_file,cls_proto_file,ClassParam,num_thread,device);
}extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_cv_tnn_model_Detector_detect(JNIEnv *env, jclass clazz, jobject bitmap,jfloat score_thresh, jfloat iou_thresh) {cv::Mat bgr;BitmapToMatrix(env, bitmap, bgr);int src_h = bgr.rows;int src_w = bgr.cols;// 检测区域为整张图片的大小FrameInfo resultInfo;// 开始检测if (detector != nullptr) {detector->detect(bgr, &resultInfo, score_thresh, iou_thresh);} else {ObjectInfo objectInfo;objectInfo.x1 = 0;objectInfo.y1 = 0;objectInfo.x2 = (float)src_w;objectInfo.y2 = (float)src_h;objectInfo.label = 0;resultInfo.info.push_back(objectInfo);}int nums = resultInfo.info.size();LOGW("object nums: %d\n", nums);if (nums > 0) {// 开始检测classifier->detect(bgr, &resultInfo);// 可视化代码printf("sitting label:%d,score:%3.5f", resultInfo.label, resultInfo.score);//classifier->visualizeResult(bgr, &resultInfo);}//cv::cvtColor(bgr, bgr, cv::COLOR_BGR2RGB);//MatrixToBitmap(env, bgr, dst_bitmap);auto BoxInfo = env->FindClass("com/cv/tnn/model/FrameInfo");auto init_id = env->GetMethodID(BoxInfo, "<init>", "()V");auto box_id = env->GetMethodID(BoxInfo, "addBox", "(FFFFIF)V");auto ky_id = env->GetMethodID(BoxInfo, "addKeyPoint", "(FFF)V");jobjectArray ret = env->NewObjectArray(resultInfo.info.size(), BoxInfo, nullptr);for (int i = 0; i < nums; ++i) {auto info = resultInfo.info[i];env->PushLocalFrame(1);//jobject obj = env->AllocObject(BoxInfo);jobject obj = env->NewObject(BoxInfo, init_id);// set bbox//LOGW("rect:[%f,%f,%f,%f] label:%d,score:%f \n", info.rect.x,info.rect.y, info.rect.w, info.rect.h, 0, 1.0f);env->CallVoidMethod(obj, box_id, info.x1, info.y1, info.x2 - info.x1, info.y2 - info.y1,info.category.label, info.category.score);// set keypointfor (const auto &kps : info.landmarks) {//LOGW("point:[%f,%f] score:%f \n", lm.point.x, lm.point.y, lm.score);env->CallVoidMethod(obj, ky_id, (float) kps.x, (float) kps.y, 1.0f);}obj = env->PopLocalFrame(obj);env->SetObjectArrayElement(ret, i, obj);}return ret;
}

(4) Android测试效果 

Android Demo在普通手机CPU/GPU上可以达到实时检测和识别效果;CPU(4线程)约30ms左右,GPU约25ms左右 ,基本满足业务的性能需求。

   

(5) 运行APP闪退:dlopen failed: library "libomp.so" not found

参考解决方法:
解决dlopen failed: library “libomp.so“ not found_PKing666666的博客-CSDN博客_dlopen failed

 Android SDK和NDK相关版本信息,请参考: 

 


5.项目源码下载

Android项目源码下载地址:面部表情识别3:Android实现表情识别(含源码,可实时检测)

整套Android项目源码内容包含:

  1. 提供Android版本的人脸检测模型
  2. 提供面部表情识别Android Demo源码
  3. Android Demo在普通手机CPU/GPU上可以实时检测和识别,约30ms左右
  4. Android Demo支持图片,视频,摄像头测试
  5. 所有依赖库都已经配置好,可直接build运行,若运行出现闪退,请参考dlopen failed: library “libomp.so“ not found 解决。

 Android面部表情识别APP Demo体验:https://download.csdn.net/download/guyuealian/87575425

或者链接: https://pan.baidu.com/s/16OOi-qCENP4WbIeSzO5e9g 提取码: cs5g 

如果你需要面部表情识别的训练代码,请参考:《面部表情识别2:Pytorch实现表情识别(含表情识别数据集和训练代码)》https://blog.csdn.net/guyuealian/article/details/129505205

相关文章:

面部表情识别3:Android实现表情识别(含源码,可实时检测)

面部表情识别3&#xff1a;Android实现表情识别(含源码&#xff0c;可实时检测) 目录 面部表情识别3&#xff1a;Android实现表情识别(含源码&#xff0c;可实时检测) 1.面部表情识别方法 2.人脸检测方法 3.面部表情识别模型训练 4.面部表情识别模型Android部署 &#x…...

【IT女神勋章挑战赛名单公布】:看看谁获奖啦!

致敬女性开发者&#xff0c;为那些IT女神而战&#xff01;「IT女神勋章挑战赛」已落下帷幕&#xff0c;博主们的比拼结果也已新鲜出炉&#xff0c;快来看看此次征文中&#xff0c;有哪些精彩文章不容错过&#xff01;活动官网&#xff1a;https://activity.csdn.net/creatActiv…...

ThreadPool线程池源码解析

ThreadPool线程池源码解析 文章目录前言一、基本使用二、执行流程三、源码分析ThreadPoolExecutor 中重要属性ThreadPoolExecutor 内部类Workerexecute&#xff08;&#xff09;方法addWorker(command, true)方法runWorker(worker )方法getTask()方法shutdown和shutdownNow四、…...

Python中 5个非常有用的单行代码

Python中 5个非常有用的单行代码1. 什么是单行代码&#xff1f;2. 了解和使用单行代码的好处3. 5个单行代码示例参考作为开发人员&#xff0c;你想提高生产力吗&#xff1f;掌握这 5 个 Python 一行代码&#xff0c;你就能写出简洁的代码。 这篇博客将介绍5个非常有用的初学者可…...

蓝牙模块各种工作模式

摘要&#xff1a;本文主要归纳总结蓝牙模块的不同工作模式&#xff0c;通过蓝牙模块不同的工作模式了解其扮演不同角色时工作的一个基本原理&#xff0c;为更深入的研究蓝牙模块底层的工作机制和技术方案进行铺垫。 1、主设备工作模式 主设备是能够搜索别人并主动建立连接的一…...

修剪灌木[蓝桥杯2022初赛]

题目描述 爱丽丝要完成一项修剪灌木的工作。 有 N 棵灌木整齐的从左到右排成一排。 爱丽丝在每天傍晚会修剪一棵灌木&#xff0c;让灌木的高度变为 0 厘米。 爱丽丝修剪灌木的顺序是从最左侧的灌木开始&#xff0c;每天向右修剪一棵灌木。 当修剪了最右侧的灌木后&#xff0c;…...

Python的知识点运用-1(日期转换)

问&#xff1a;如何将 星期一, 三月 13, 2023转换成2023-03-13看到这个问题&#xff0c;你的第一反应是什么&#xff1f;&#xff1f;&#xff1f;反正我是懵逼的。不过后面一想&#xff0c;时间模块可以。在这个问题后面&#xff0c;群友又问了一个问题&#xff0c;如何在本地…...

原理图制图规范详细说明

1、1 原理图必须使用公司统一原理图库 在原理图设计中&#xff0c;必须采用公司统一原理图库&#xff0c;以保证设计的一致性和打包后封装、料单等结果的一致性。不使用公司统一原理图库造成的连接、封装错误个人承担责任。 注意使cds.lib中的路径指向库服务器eda-svr1的路径…...

【Unity小知识】Editor编写常用方法汇总

汇总一些Unity Editor开发的常用方法和实现方式&#xff0c;会持续更新。 添加自定义菜单栏方法 using UnityEngine; using UnityEditor;public class EditorTools : EditorWindow {[MenuItem("EditorTools/自定义的编辑器方法")]public static void CustomEditroFu…...

【数据仓库-4】-- 提取、转换、装载(ETL)

1.数据抽取 1.1 逻辑抽取 1.1.1 全量数据抽取 一般发生在我们初始化时,需要一次性将源库(业务系统)的所有数据抽取到数据仓库的ODS层。 比如将一个全表导出数据文件或者查询源表所有数据的SQL语句, 都是全量抽取的例子。 select * from user.person; 1.1.2 增量数据抽取 对…...

【DBC专题】-12-不同类型报文(应用/诊断/网关/测量标定)在DBC中配置,以及在Autosar各模块间的信号数据流向

点击返回「Autosar从入门到精通-实战篇」总目录 案例背景(共18页精讲)&#xff1a;该篇博文将告诉您&#xff1a; 1)Autosar中&#xff0c;不同类型报文(App应用&#xff0c;UDS/OBD诊断&#xff0c;NM网络管理报文&#xff0c;XCP测量标定)的信号数据流向&#xff1b; 2)CAN …...

【Linux】进程的基础概念 进程的相关操作 进程的状态

进程一、进程的基本知识1、基本概念2、进程的描述 —— PCB3、task_ struct内容分类二、进程的相关操作1、在Linux下查看进程2、通过系统调用在代码中获取进程标示符3、如何创建子进程4、关于fork()的一些深度理解三、进程的状态Linux中的进程的状态四、僵尸进程与孤儿进程僵尸…...

【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块

【小猫爪】AUTOSAR学习笔记06-Communication Stack之ComM模块前言1 ComM简介2 ComM功能介绍2.1 PNC 状态管理2.2 Channel状态管理2.3 通信禁止功能2.4 不同类型的NM2.5 User、PNC 与 Channel 的映射2.6 状态保存END前言 因为一个偶然的机会让我接触到了AUTOSAR&#xff0c;所以…...

BP插件开发(JAVA)

本文会包括BP插件开发流程及打包&#xff0c;API&#xff0c;javaswing&#xff08;UI&#xff09;部分的内容。阅读完本文后&#xff0c;读者将初步具有开发BP插件的能力。1 开始开发我们使用IDEA作为开发工具&#xff08;使用其他IDE也绰绰有余&#xff09;。引入依赖包&…...

【Zookeeper】介绍与配置

目录 概述 工作机制 特点 数据结构 应用场景 统一配置管理 统一集群管理 ​编辑 服务器动态上下线 软负载均衡 下载 启动 启动客户端 配置参数 集群配置 配置服务器编号 配置zoo.cfg 分发zoo.cfg配置文件 选举机制 第一次启动 非第一次启动 集群启动停止脚本…...

chrome快捷键

Ctrl T&#xff1a;打开新标签页。Ctrl W&#xff1a;关闭当前标签页。Ctrl Shift T&#xff1a;重新打开最近关闭的标签页。Ctrl Tab&#xff1a;在打开的标签页之间切换。Ctrl Shift Tab&#xff1a;在打开的标签页之间反向切换。Ctrl N&#xff1a;打开新窗口。Ctrl…...

手搓string类

手搓string类 文章目录手搓string类string的成员一.构造&#xff0c;拷贝构造&#xff0c;赋值重载和析构1.构造函数2.拷贝构造3.swap问题4.赋值重载5.析构函数二.简单接口1.c_str2.size(有效字符长度)3.capacity&#xff08;有效字符容量&#xff09;4.operator[]5.迭代器和范…...

小白学Pytorch系列--Torch API (7)

小白学Pytorch系列–Torch API (7) Comparison Ops allclose 此函数检查输入和其他是否满足条件&#xff1a; >>> torch.allclose(torch.tensor([10000., 1e-07]), torch.tensor([10000.1, 1e-08])) False >>> torch.allclose(torch.tensor([10000., 1e-…...

函数(上)——“Python”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是Python的函数呀&#xff0c;下面&#xff0c;就让我们进入函数的世界吧 首先可以选择性地看一下小雅兰很久之前写的C语言函数章节的知识&#xff1a; 函数——“C”_认真学习的小雅兰.的博客-CSDN博客 函数递归&#xf…...

ChatGPT说:如何利用ChatGPT变现?躺着赚钱不是梦。

您好&#xff0c;我是码农飞哥&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

以太网PHY布局布线指南

1. 简介 对于以太网布局布线遵循以下准则很重要&#xff0c;因为这将有助于减少信号发射&#xff0c;最大程度地减少噪声&#xff0c;确保器件作用&#xff0c;最大程度地减少泄漏并提高信号质量。 2. PHY设计准则 2.1 DRC错误检查 首先检查DRC规则是否设置正确&#xff0c;然…...