【智能家居】八、监控摄像采集、人脸识别比对进行开门功能点

一、使用 fswebcam 测试 USB 摄像头
二、根据demo来实现功能点
三、功能点编写编译运行实现
四、mjpg实现监控识别
五、V4L2 视频设备 Linux 内核模块的一部分

一、使用 fswebcam 测试 USB 摄像头

a. 安装 fswebcam

orangepi@orangepi:~$ sudo apt update
orangepi@orangepi:~$ sudo apt-get install -y fswebcam

b. 安装完 fswebcam 后可以使用下面的命令来拍照

a) -d 选项用于指定 USB 摄像头的设备节点
b) --no-banner 用于去除照片的水印
c) -r 选项用于指定照片的分辨率
d) -S 选项用设置于跳过前面的帧数
e) ./image.jpg 用于设置生成的照片的名字和路径

orangepi@orangepi:~$ sudo fswebcam -d /dev/video0 \ --no-banner -r 1280x720 -S 5 ./image.jpg

c. 在服务器版的 linux 系统中,拍完照后可以使用 scp 命令将拍好的图片传到
Ubuntu PC 上镜像观看

orangepi@orangepi:~$ scp image.jpg test@192.168.1.55:/home/test(根据实际情况修改 IP 地址和路径)

d. 在桌面版的 linux 系统中,可以通过 HDMI 显示器直接查看拍摄的图片

这里使用fswebcam进行拍照。参考用户手册

首先在/smart_home拍照,命名为imageComp.jpg

sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./imageComp.jpg

二、根据demo来实现功能点

demo.c

主要的功能是通过摄像头采集人脸数据,然后通过 cURL 发送 POST 请求到指定的 API 接口,接收 OCR 后台返回的数据。在这个过程中,你使用了一些全局变量、文件 I/O、cURL 库等。

以下是一些建议和注意事项:

  1. 错误处理: 在系统调用和库函数调用后,最好检查其返回值,以确保操作成功。例如,你可以在文件打开、内存分配等操作后添加错误检查,并在失败时输出错误信息。

  2. 函数封装: 考虑将一些相关的操作封装成函数,以提高代码的模块性和可读性。例如,可以将 cURL 相关的初始化和清理操作封装成函数。

  3. 全局变量的使用: 全局变量在函数间传递数据,但过度使用全局变量可能导致代码难以理解和维护。尽量将数据传递作为函数参数,以提高函数的可复用性。

  4. 内存释放: 在使用 malloc 分配内存后,确保在不再需要使用该内存时调用 free 进行释放,以避免内存泄漏。

  5. 字符串操作: 在使用字符串拼接函数(如 sprintf)时,确保目标缓冲区足够大以防止缓冲区溢出。

  6. 资源释放顺序: 在释放资源时,注意释放的顺序,以避免悬挂指针或资源泄漏。

  7. 安全性: 尽量避免使用 system 函数来执行外部命令,这可能带来安全风险。如果可能的话,考虑使用更安全的库函数或 API。

下面是一些可能的改进:

// 错误处理函数
void handleError(const char *message)
{perror(message);exit(EXIT_FAILURE);
}// 初始化 cURL
CURL *initCurl()
{CURL *curl = curl_easy_init();if (!curl){handleError("curl_easy_init failed");}return curl;
}// 释放 cURL 资源
void cleanupCurl(CURL *curl)
{curl_easy_cleanup(curl);
}// 发送 cURL POST 请求
bool sendPostRequest(const char *url, const char *postString, size_t (*writeCallback)(void *, size_t, size_t, void *))
{CURL *curl = initCurl();if (!curl){return false;}CURLcode res;// 设置 cURL 选项curl_easy_setopt(curl, CURLOPT_URL, url);curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);// 执行请求res = curl_easy_perform(curl);// 检查执行结果if (res != CURLE_OK){fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));cleanupCurl(curl);return false;}cleanupCurl(curl);return true;
}// 获取人脸数据
char *getFace()
{printf("人脸数据采集中...\n");system("sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 ./image.jpg");while (access("./image.jpg", F_OK) != 0){// 等待文件存在}printf("数据采集完毕\n");// 获取 base64 数据char *base64BufFaceRec = getBase64FromFile("./image.jpg");remove("./image.jpg"); // 采集完成删除,防止占内存return base64BufFaceRec;
}// 发送人脸数据到 OCR 后台
bool sendFaceToOcr()
{char *img1 = getFace(); // 图片 base64 流char *img2 = getBase64FromFile("/home/orangepi/smart_home/test/imageComp.jpg");char *key = "JFD5c1iBh9LVqPkkZMqM8Q";char *secret ="76f444813fc945bd9543e4d7e086ad87";int typeId = 21;char *format = "xml";// 分配足够的空间int len = strlen(key) + strlen(secret) + strlen(img1) + strlen(img2) + 128;char *postString = (char *)malloc(len);if (!postString){handleError("Memory allocation failed");}// 字符串拼接snprintf(postString, len, "img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);// 发送 POST 请求bool result = sendPostRequest("https://netocr.com/api/faceliu.do", postString, readData);// 释放动态分配的内存free(img1);free(img2);free(postString);return result;
}// 入口函数
int main(void)
{// 发送人脸数据到 OCR 后台if (sendFaceToOcr()){// 处理 OCR 后台返回的数据// ...}return 0;
}

这个版本进行了一些重构,提高了可读性和模块性,同时加入了错误处理。你可以根据需要进一步调整。

三、功能点编写编译运行实现

camera.c

#include "controlDevice.h"
#include <stdio.h>
#include <curl/curl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>char ocrReturnBuf[1024] = {'\0'}; // 全局变量,用来接收从OCR后台返回的数据// 回调函数,读取从OCR后台返回的数据(把从后台的数据拷贝给ocrReturnBuf)
size_t readData(void *ptr, size_t size, size_t nmemb, void *stream)
{size_t totalSize = size * nmemb;// 为了避免溢出,计算实际拷贝的长度size_t copySize = (totalSize < (sizeof(ocrReturnBuf) - 1)) ? totalSize : (sizeof(ocrReturnBuf) - 1);// 拷贝数据到 ocrRetBuf 中memcpy(ocrReturnBuf, ptr, copySize);// 手动添加字符串终结符ocrReturnBuf[copySize] = '\0';return totalSize;
}char *getBase64FromFile(const char *filePath)
{char cmd[256] = {'\0'};char *base64Buf = NULL;// 使用安全的方式构建命令snprintf(cmd, sizeof(cmd), "base64 %s | tr -d '\n' > tmpFile", filePath);if (system(cmd) == -1) {perror("Error executing system command");return NULL;}int fd = open("./tmpFile", O_RDWR);if (fd == -1) {perror("Error opening file");return NULL;}// 计算文件大小int fileLen = lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET);// 动态分配内存base64Buf = (char *)malloc(fileLen + 1);if (base64Buf == NULL) {perror("Error allocating memory");close(fd);return NULL;}memset(base64Buf, '\0', fileLen + 1);// 读取文件内容到字符串if (read(fd, base64Buf, fileLen) == -1) {perror("Error reading file");free(base64Buf);close(fd);return NULL;}close(fd);// 删除临时文件if (remove("tmpFile") == -1) {perror("Error deleting temporary file");}return base64Buf;
}// 获取人脸数据
char *getFace()
{printf("人脸数据采集中...\n");system("sudo fswebcam -d /dev/video0 --no-banner -r 1280x720 -S 5 /home/orangepi/smart_home/test/image.jpg");while (access("./image.jpg", F_OK) != 0); // 判断是否拍照完毕printf("数据采集完毕\n");// 获取 base64 数据char *base64BufFaceRec = getBase64FromFile("./image.jpg");remove("./image.jpg");   // 采集完成删除,防止占内存return base64BufFaceRec; // 返回刚才拍照的base64
}// 根据文档,接口调用方法为post请求
void postUrl()
{CURL *curl;CURLcode res;// 根据翔云平台的接口要求  分开定义,然后字符串拼接char *img1 = getFace(); // 图片base64流char *img2 = getBase64FromFile("/home/orangepi/smart_home/test/imageComp.jpg");char *key = "JFD5c1iBh9LVqPkkZMqM8Q";char *secret = "76f444813fc945bd9543e4d7e086ad87";int typeId = 21;char *format = "xml";int len = strlen(key) + strlen(secret) + strlen(img1) + strlen(img2) + 128; // 分配空间不够会>导致栈溢出char* postString = (char*)malloc(len);memset(postString, '\0', len);//因为postString是一个指针,不能用sizeof来计算其指向的大小// 字符串拼接sprintf(postString, "img1=%s&img2=%s&key=%s&secret=%s&typeId=%d&format=%s", img1, img2, key, secret, typeId, format);// 初始化 cURLcurl = curl_easy_init();if (curl){// 指定cookie缓存文件// if (curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt") != CURLE_OK)// {//         fprintf(stderr, "Failed to set cookie file\n");//         return false; // 在设置失败时,直接返回// }// 指定post传输内容,get请求将URL和postString一次性发送curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postString);// 指定urlcurl_easy_setopt(curl, CURLOPT_URL, "https://netocr.com/api/faceliu.do");// 回调函数读取返回值curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData);// 执行请求res = curl_easy_perform(curl);if (res != CURLE_OK) {fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));// 处理错误}// 释放 cURL 资源curl_easy_cleanup(curl);}// 释放动态分配的内存free(img1);free(img2);free(postString);
}struct Devices camera = {.deviceName = "camera",.justDoOnce = postUrl
};struct Devices *addCameraToDeviceLink(struct Devices *phead)
{if (phead == NULL) {return &camera;}else {camera.next = phead; // 以前的头变成.nextphead = &camera;     // 更新头return phead;}
}

controlDevice.h

#include <wiringPi.h>					//wiringPi库
#include <stdio.h>
#include <stdlib.h>// 设备结构体
struct Devices                          //设备类
{char deviceName[128];               //设备名int status;                         //状态int pinNum;							//引脚号// 函数指针,用于设备控制int (*Init)(int pinNum);			//“初始化设备”函数指针int (*open)(int pinNum);			//“打开设备”函数指针int (*close)(int pinNum);			//“关闭设备”函数指针int (*readStatus)(int pinNum);		//“读取设备状态”函数指针  为火灾报警器准备int (*changeStatus)(int status);	//“改变设备状态”函数指针void (*justDoOnce)();               // 仅执行一次的操作struct Devices *next;
};struct Devices* addBathroomLightToDeviceLink(struct Devices *phead);		//“浴室灯”加入设备链表函数声明      2
struct Devices* addBedroomLightToDeviceLink(struct Devices *phead);	        //“卧室灯”加入设备链表函数声明      8
struct Devices* addRestaurantLightToDeviceLink(struct Devices *phead);		//“餐厅灯”加入设备链表函数声明      13
struct Devices* addLivingroomLightToDeviceLink(struct Devices *phead);		//“客厅灯”加入设备链表函数声明      16
struct Devices* addSmokeAlarmToDeviceLink(struct Devices *phead);           //“烟雾报警器”加入设备链表函数声明  6
struct Devices* addBuzzerToDeviceLink(struct Devices *phead);		        //“蜂鸣器”加入设备链表函数声明      9
struct Devices *addCameraToDeviceLink(struct Devices *phead);               // “摄像头”加入设备链表
struct Devices *addLockToDeviceLink(struct Devices *phead);                 // “门锁”加入设备链表			   15

main.c

在main.c文件里的Command(struct InputCommand* CmdHandler)函数中添加

    // OCR 指令:执行人脸识别功能进行开门if (strcmp("OCR", CmdHandler->command) == 0){tmp = findDeviceByName("camera", pdeviceHead);if (tmp != NULL){tmp->justDoOnce();// 字符串检索 判断翔云后台返回的一大堆字符串中有没有“否”if (strstr(ocrReturnBuf, "否") != NULL){printf("人脸比对失败\n");}else{printf("人脸比对成功\n");tmp = findDeviceByName("lock", pdeviceHead);if (tmp != NULL){tmp->open(tmp->pinNum);printf("已开门\n");delay(3000);tmp->close(tmp->pinNum);}}}}

这里的摄像头只是当作一个设备去用,目前实现通过串口指令然后system()进行拍照。然后翔云平台进行人脸对比,未实现自动人脸检测。

所以摄像头没有另创线程。但是做视频监控可以另创线程。

这样当串口发送OCR时,实现人脸对比并开锁,所以没有用线程去做

当然,要把camera、lock设备加入设备工厂

注意:ocrReturnBuf这个因为要再不同文件调用的全局变量所以要extern

编译运行

gcc *.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -I /home/orangepi/curl-7.71.1/_install/include/ -L /home/orangepi/curl-7.71.1/_install/lib/ -lcurlsudo -E ./a.out

在这里插入图片描述

运行结果

在这里插入图片描述

四、mjpg实现监控识别

mjpg来实现也是一样的
通过前面智能垃圾分类章节【阿里云】图像识别 摄像模块 语音模块
在这里插入图片描述
在这里插入图片描述

五、V4L2 视频设备 Linux 内核模块的一部分

V4L2 是视频设备 Linux 内核模块的一部分,全名是 Video for Linux 2。它提供了一套标准的 API,用于控制和管理视频设备,比如摄像头、视频捕获卡等。V4L2 的设计旨在与 Linux 内核无缝集成,并提供用户空间应用程序与视频设备进行交互的标准接口。

V4L2 的主要特性和功能包括:

  1. 设备的打开和关闭: 使用 V4L2,可以打开和关闭视频设备。

  2. 格式和尺寸控制: V4L2 允许应用程序查询和设置视频设备支持的不同格式和分辨率。

  3. 帧缓冲管理: 应用程序可以通过 V4L2 分配、映射和取消映射帧缓冲。

  4. 视频捕获和输出: V4L2 允许应用程序启动视频捕获或输出操作,并控制捕获或输出的参数。

  5. 控制操作: V4L2 提供了对摄像头参数(如亮度、对比度、色彩饱和度等)的控制。

  6. 流 IO 操作: 支持单帧和多帧的 I/O 操作,用于捕获或输出视频流。

  7. 回调函数: V4L2 支持回调函数,使得应用程序可以在特定事件发生时得到通知。

V4L2 API 的使用一般包括在用户空间的应用程序中调用相应的系统调用,例如 open()ioctl() 等,以与视频设备进行交互。在内核空间,V4L2 的实现则通过提供相应的结构体和函数指针来支持。

请注意,V4L2 的详细定义和使用方式可能会根据 Linux 内核版本的不同而有所变化,因此建议查阅相应版本的内核文档和头文件以获取准确的信息。

在 Linux 上,安装和配置 V4L2 通常涉及以下步骤:

  1. 检查内核支持: 确保你的 Linux 内核已启用 V4L2 支持。通常,大多数标准的 Linux 内核都包含了 V4L2 模块。你可以通过查看内核配置文件或使用 lsmod | grep videodev 命令来检查是否加载了 videodev 模块。

  2. 安装 V4L2 工具: 有一些工具可用于测试和配置 V4L2 设备。其中一个常用的工具是 v4l-utils。你可以使用包管理工具安装它,例如:

    sudo apt-get install v4l-utils   # 对于基于 Debian 的系统
    

    sudo yum install v4l-utils   # 对于基于 Red Hat 的系统
    
  3. 配置设备权限: 确保用户具有访问视频设备的权限。你可以将用户添加到 video 组,或者通过修改设备文件的权限来实现。设备文件通常在 /dev 目录下,例如 /dev/video0

  4. 加载 V4L2 模块: 如果你的内核未自动加载 V4L2 模块,你可以使用 modprobe 命令手动加载:

    sudo modprobe videodev
    

    或者,如果你使用的是特定的摄像头或设备驱动程序,可能需要加载相关的模块。

  5. 测试设备: 使用 v4l2-ctl 工具或其他 V4L2 相关工具测试你的视频设备。例如,你可以使用以下命令查看设备的基本信息:

    v4l2-ctl --list-devices
    

    或者使用以下命令查看摄像头的支持格式和参数:

    v4l2-ctl --list-formats-ext -d /dev/video0
    

这些步骤提供了一个基本的 V4L2 安装和配置的概述。具体的步骤可能会因你的系统和设备而有所不同。请查阅相关的文档和手册以获取更详细的信息。

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

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

相关文章

SSL证书可以有效避免劫持和钓鱼攻击

SSL证书对抗劫持和钓鱼攻击起到重要作用。通过使用SSL证书&#xff0c;网站可以防止黑客篡改网站内容或欺骗用户&#xff0c;确保用户所看到的是真实的网站信息&#xff0c;而不是被篡改的内容或诱导性的信息。这有助于保护用户的隐私和安全&#xff0c;并建立用户对网站的信任…

LeetCode008之字符串转换整数 (相关话题:状态机)

题目描述 请你来实现一个 myAtoi(string s) 函数&#xff0c;使其能将字符串转换成一个 32 位有符号整数&#xff08;类似 C/C 中的 atoi 函数&#xff09;。 函数 myAtoi(string s) 的算法如下&#xff1a; 读入字符串并丢弃无用的前导空格检查下一个字符&#xff08;假设还…

一天搞定jmeter入门到入职全套教程之Jmeter分布式测试

随着并发量的增大&#xff0c;一台机器就不能满足需求了&#xff0c;所以我们采用分布式&#xff08;Master-Slaver&#xff09;的方案去执行高并发的测试 注意事项&#xff1a; Master机器一般我们不执测试&#xff0c;所以可以拿一台配置差些的机器&#xff0c;主要用来采集…

c 语言 堆的解析(自我理解)!!!堆排序,建堆

目录 1.堆是什么&#xff1f; 2.堆的实现和接口。&#xff08;小堆&#xff09; 1.头文件 2.初始化 3.摧毁 4.向上调整&#xff08;重点&#xff09; 5.向下调整&#xff08;重点&#xff09; 6.插入&#xff08;重点&#xff09; 7.删除&#xff08;重点&#xff09; …

Linux---虚拟机软件

1. 虚拟机软件的介绍 它是能够虚拟出来计算机的一个软件。 常用虚拟机软件: VmwareVirtualBox 说明: 只有安装了虚拟机软件才可以创建虚拟机&#xff0c;当然通过虚拟机软件还可以创建多个虚拟机。 2. 虚拟机的介绍 就是模拟一个真实的计算机&#xff0c;好比一个虚拟的…

go学习之反射知识

反射 文章目录 反射1、反射的使用场景1&#xff09;结构体标签的应用2&#xff09;使用反射机制编写函数的适配器&#xff08;桥连接&#xff09; 2、反射的基本介绍-1.基本介绍-2.反射的图解-3.反射重要的函数和概念 3.反射快速入门-1.请编写一个函数&#xff0c;演示对&#…

【C进阶】C程序是怎么运作的呢?-- 程序环境和预处理(下)

前言&#xff1a; 这是程序环境和预处理的下半篇文章。至此&#xff0c;关于c语言知识点:从编译到运行的过程已讲解完毕。传送&#x1f6aa;&#xff0c;上半篇&#xff1a; http://t.csdnimg.cn/hvxmr 本章涉及的知识点&#xff1a; 宏和函数对比、命名约定、#undef、命令行定…

C语言—每日选择题—Day46

第一题 1. 下列程序段的输出结果是&#xff08;&#xff09; #include <stdio.h> int main() {int x 1,a 0,b 0;switch(x) {case 0: b;case 1: a;case 2: a;b;}printf("a%d,b%d\n", a, b);return 0; } A&#xff1a;a2,b1 B&#xff1a;a1,b1 C&#xf…

JVM面试

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.JVM 的整体结构2.类加载做了哪些事情?类加载器有哪些&#xff1f;双亲委派和沙箱安全 3.Java虚拟机栈是什么4.方法区的理解HotSpot 中方法区的演进方法区的内部结…

【Spring教程26】Spring框架实战:从零开始学习SpringMVC 之 bean加载控制

目录 1 问题分析2 思路分析3 环境准备4 设置bean加载控制5 知识点1&#xff1a;ComponentScan 欢迎大家回到《Java教程之Spring30天快速入门》&#xff0c;本教程所有示例均基于Maven实现&#xff0c;如果您对Maven还很陌生&#xff0c;请移步本人的博文《如何在windows11下安装…

文件搜索项目演示

演示功能搜索功能1&#xff1a;根据文件名搜索2&#xff1a;根据文件路径搜索3&#xff1a;根据文件名拼音(全拼、首拼)搜索 选择更新目录功能自动初始化和定时更新功能程序文件项目知识介绍 演示功能 搜索功能 1&#xff1a;根据文件名搜索 2&#xff1a;根据文件路径搜索 3…

Linux--操作系统

1. 常见的操作系统 Windowsmac OSLinuxiOSAndroid 2. 操作系统的定义 操作系统直接运行在计算机上的系统软件&#xff0c; 它是控制硬件和支持软件运行的计算机程序。 3. 操作系统的作用 向下控制硬件向上支持软件的运行&#xff0c;具有承上启下的作用。 4.总结 操作系统…

Win10的SVN Adapter V1.0 中黄色感叹号 -- 解决

大部分都问题都可以通过&#xff1a; 关闭 SVN Adapter V1.0 在下载最新的 SVNDrv.sys替换 C:\Windows\System32\drivers 中的同名文件启动 SVN Adapter V1.0 就能成功 但是部分人的电脑 SVN Adapter V1.0 是有感叹号的&#xff0c;说明注册表有问题 先用 CCleaner 修复注册表…

vue实现移动端适配

目录 1. 使用vw单位&#xff1a;vw是视窗宽度的百分比&#xff0c;可以根据不同设备的屏幕宽度来进行自适应。在Vue中可以通过设置全局CSS样式&#xff0c;将所有的尺寸单位改为vw。 2. 使用Flexible.js&#xff1a;Flexible.js是一个用于淘宝移动端适配的库&#xff0c;可以…

正则表达式(8):基本正则表达式小结

正则表达式&#xff08;8&#xff09;&#xff1a;基本正则表达式小结 本博文转载自 写这篇文章的目的就是总结前文中所介绍的”基本正则表达式”&#xff0c;并且结合一些实例进行练习&#xff0c;以便我们能够在练习中完全掌握它们。 首先&#xff0c;我们对前文中提到的符…

【rabbitMQ】声明队列和交换机

上一篇&#xff1a;springboot整合rabbitMQ模拟简单收发消息 https://blog.csdn.net/m0_67930426/article/details/134904766?spm1001.2014.3001.5501 相关配置环境参考上篇 springAMQP提供了几个类用来声明声明队列&#xff0c;交换机及其绑定关系 声明队列&#xff0c;…

Rational rose 安装教程(图文)

Rational Rose是Rational公司出品的一种面向对象的统一建模语言的可视化建模工具。用于可视化建模和公司级水平软件应用的组件构造。 就像一个戏剧导演设计一个剧本一样&#xff0c;一个软件设计师使用Rational Rose&#xff0c;以演员&#xff08;数字&#xff09;、使用拖放式…

第三届iEnglish全国ETP大赛16强落位 诠释教育游戏价值

10日,与北方骤降的温度形成鲜明对比,以“玩转英语,用iEnglish”为主题的国内首个教育游戏活动第三届iEnglish全国ETP(English Through Pictures)大赛总决赛小组赛热火朝天的进行。随着“云帆沧海队”搭上末班车,本届活动16强全部产生,接下来的三个周末他们将向年度总冠军发起最…

C# WPF上位机开发(增强版绘图软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们写过一个绘图软件&#xff0c;不过那个比较简单&#xff0c;主要就是用鼠标模拟pen进行绘图。实际应用中&#xff0c;另外一种使用比较多的…

Leetcode—509.斐波那契数【简单】

2023每日刷题&#xff08;五十七&#xff09; Leetcode—509.斐波那契数 实现代码 int fib(int n){if(n 0) {return 0;}if(n 1) {return 1;}return fib(n-1) fib(n-2); }运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连哦&#xff0c;点…