如何用iOS自带摄像头进行拍摄获取视频流以及OpenCV图像处理实时显示
目录
- 概述
- 一、如何用Swift调用OpenCV库
- 1.项目引入OpenCV库
- 2.桥接OpenCV及Swift
- 二、运用AVFoundation获取实时图像数据
- 1.建立视频流数据捕获框架
- 2.建立 Capture Session
- 3.取得并配置 Capture Devices
- 4.设定 Device Inputs
- 5.配置Video Data Output输出
- 6.工程隐私权限配置
- 7.处理相机视频回调
- 三、视频流原始数据CMSampleBuffer处理
- 1.CMSampleBuffer数据转换为Mat数据
- 2.回调中的数据处理
- 3.Mat数据转换为UIImage数据用于显示
- 四、Swift界面搭建
- 1.在UI层捕获相机数据
- 2.直接显示CMSampleBuffer方法
- 五、基于Object-C++的OpenCV图像处理部分
- 1.引入头文件
- 2.OpenCV人脸识别输出识别框
- 总结
概述
在2020年6月9日之后,OpenCV可以直接在Objective-C和Swift中使用它,而无需自己编写Objective-C++,可以直接在OpenCV官网下载iOS Package包,使用起来也是比较简单。但由于之前对OpenCV库的使用是使用C++编写,所以Objective-C++在图像处理部分使用起来更顺手,因此本文主要的技术框架是使用Objective-C++编写图像处理流程,Swift编写iOS界面及AVFoundation相机等的调用以获取实时的图像数据。本文主要以实时框出人脸为示例,iOS移动端界面的显示结果大致如下图。
OpenCV官网:https://opencv.org/releases/
一、如何用Swift调用OpenCV库
1.项目引入OpenCV库
- 使用cocoapods就非常简单:
pod 'OpenCV'
- 自行手动添加:在官网下载相应版本的iOS Pack,解压后得到一个 opencv2.framework 库,创建项目并右键添加文件到项目。
2.桥接OpenCV及Swift
- 前面说到OpenCV框架是用C++进行编程的,因此要用Objective-C++代码于Swift代码进行桥接。首先添加一个 Objective-C 文件到项目中,会弹出一个是否添加 Bridging-Header 文件,选择添加(若此处没弹出,则可以手动添加Bridging-Header 文件,即添加一个头文件(Header file),重命名为“项目名-Bridging-Header.h”),这就实现了Swift和Object-C的混编。
- 将这个Object-C的文件扩展名“.m”改为“.mm”这就将该文件变成了Objective-C++文件,文件大致如下
二、运用AVFoundation获取实时图像数据
Apple预设的APIs 如UIImagePickerController能够直接获取摄像头获取的图像并显示在界面上,操作简单,但无法对原数据进行操作,因此本文中应用AVFoundation的 Capture Sessions来采集图像和视频流。根据官方文档,Capture Session 是用以【管理采集活动、并协调来自 Input Devices 到采集 Outputs 的数据流】。在 AVFoundation 内,Capture Sessions 是由AVCaptureSession来管理的。
1.建立视频流数据捕获框架
首先创建一个NSObject类型的Controller名为CameraController,处理摄像头的事务,设置prepare函数以供主程序调用,其主要负责设立一个新的 Capture Session。设定 Capture Session 分为五个步骤:
- 建立一个 Capture Session
- 取得并配置 Capture Devices
- 在 Capture Device 上建立 Inputs
- 设置一个 Video Data Output 物件
- 配置Video Data Output Queue参数
func prepare(completionHandler: @escaping (Error?) -> Void) {//建立一个 Capture Sessionfunc createCaptureSession() { }//取得并配置 Capture Devicesfunc configureCaptureDevices() throws { }//在 Capture Device 上建立 Inputsfunc configureDeviceInputs() throws { }//设置一个 Video Data Output 物件func configureVideoDataOutput() throws { }//配置Video Data Output Queue参数func configureVideoDataOutputQueue() throws{ }DispatchQueue(label: "prepare").async {do {createCaptureSession()try configureCaptureDevices()try configureDeviceInputs()try configureVideoDataOutput()try configureVideoDataOutputQueue()}catch {DispatchQueue.main.async {completionHandler(error)} return}DispatchQueue.main.async {completionHandler(nil)}}
}
2.建立 Capture Session
建立新的AVCaptureSession,并将它存储在captureSession的属性里,并设定一些用于抛出的错误类型
var captureSession: AVCaptureSession?func createCaptureSession() { self.captureSession = AVCaptureSession()
}//设定prepare过程中遇到的错误类型enum CameraControllerError: Swift.Error {case captureSessionAlreadyRunningcase captureSessionIsMissingcase inputsAreInvalidcase invalidOperationcase noCamerasAvailablecase unknown}//设定相机位置为前后相机public enum CameraPosition {case frontcase rear}
3.取得并配置 Capture Devices
建立了一个AVCaptureSession后,需要建立AVCaptureDevice物件来代表实际的相机
//前置镜头var frontCamera: AVCaptureDevice?//后置镜头var rearCamera: AVCaptureDevice?func configureCaptureDevices() throws {//使用了AVCaptureDeviceDiscoverySession找出设备上所有可用的内置相机 (`.builtInDualCamera`)。//若没找到相机则抛出异常。let session = AVCaptureDevice.DiscoverySession.init(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .unspecified) let cameras = session.devices.compactMap { $0 }guard !cameras.isEmpty else { throw CameraControllerError.noCamerasAvailable }//遍历前面找到的可用相机,分辨出前后相机。//然后,将该相机设定为自动对焦,遇到任何问题也会抛出异常。for camera in cameras {if camera.position == .front {self.frontCamera = camera}if camera.position == .back {self.rearCamera = cameratry camera.lockForConfiguration()camera.focusMode = .continuousAutoFocuscamera.unlockForConfiguration()}}}
4.设定 Device Inputs
var currentCameraPosition: CameraPosition?
var frontCameraInput: AVCaptureDeviceInput?
var rearCameraInput: AVCaptureDeviceInput?func configureDeviceInputs() throws {//确认`captureSession`是否存在,若不存在抛出异常guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }//建立所需的 Capture Device Input 来进行数据采集。//`AVFoundation`每一次 Capture Session 仅能允许一台相机输入。//由于装置的初始设定为后相相机。先尝试用后相机 Input,再加到 Capture Session;if let rearCamera = self.rearCamera {self.rearCameraInput = try AVCaptureDeviceInput(device: rearCamera)if captureSession.canAddInput(self.rearCameraInput!) { captureSession.addInput(self.rearCameraInput!) }self.currentCameraPosition = .rear}//尝试建立前相机Input else if let frontCamera = self.frontCamera {self.frontCameraInput = try AVCaptureDeviceInput(device: frontCamera)if captureSession.canAddInput(self.frontCameraInput!) { captureSession.addInput(self.frontCameraInput!) }else { throw CameraControllerError.inputsAreInvalid }self.currentCameraPosition = .front}else { throw CameraControllerError.noCamerasAvailable }
}
5.配置Video Data Output输出
var videoOutput: AVCaptureVideoDataOutput?//配置相机的视频输出,并开始func configureVideoDataOutput() throws {guard let captureSession = self.captureSession else { throw CameraControllerError.captureSessionIsMissing }self.videoOutput = AVCaptureVideoDataOutput()if captureSession.canAddOutput(self.videoOutput!) { captureSession.addOutput(self.videoOutput!) }captureSession.startRunning()}//配置视频的输出代理及输出格式func configureVideoDataOutputQueue() throws{let videoDataOutputQueue = DispatchQueue(label: "videoDataOutputQueue")self.videoOutput!.setSampleBufferDelegate(self, queue: videoDataOutputQueue)self.videoOutput!.alwaysDiscardsLateVideoFrames = falselet BGRA32PixelFormat = NSNumber(value: Int32(kCVPixelFormatType_32BGRA))let rgbOutputSetting = [kCVPixelBufferPixelFormatTypeKey.string : BGRA32PixelFormat]self.videoOutput!.videoSettings = rgbOutputSetting}
6.工程隐私权限配置
根据Apple 规定的安全性要求,必须提供一个app使用相机权限的原因。在工程的Info.plist,加入下图的设置:
7.处理相机视频回调
能够从下方的回调中得到相机返回的实时数据,格式为CMSampleBuffer,该视频流格式不止包含图像信息还包含时间戳信息等,若想通过opencv进行处理还需进行数据转换。
extension CameraController: AVCaptureVideoDataOutputSampleBufferDelegate{func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {}
}
参考地址:https://www.appcoda.com.tw/avfoundation-camera-app/
三、视频流原始数据CMSampleBuffer处理
1.CMSampleBuffer数据转换为Mat数据
OpenCV提供了UIImageToMat的函数,根据这个思路,我们应当将CMSampleBuffer转换为UIImage数据,CMSsampleBuffer不止包含ImageBuffer,通过API自带的CMSampleBufferGetImageBuffer(),可以得到与我们希望得到的图像数据更为接近的cvPixelBuffer。
总的来说,下方是CMSampleBuffer转换为UIImage的两种方式,第一种通过CIImage第二种通过CGImage,通过CIImage转换成的UIImage虽然能显示在UIImageVIew上,但是在转换成Mat格式的时候会报错,因此选用第二种通过CGImage的转换。最后调用opencv库的UIImageToMat函数便能得到Mat数据了。
func image(orientation: UIImage.Orientation = .up, scale: CGFloat = 1.0) -> UIImage? {if let buffer = CMSampleBufferGetImageBuffer(self) {let ciImage = CIImage(cvPixelBuffer: buffer)return UIImage(ciImage: ciImage, scale: scale, orientation: orientation)}return nil}func imageWithCGImage(orientation: UIImage.Orientation = .up, scale: CGFloat = 1.0) -> UIImage? {if let buffer = CMSampleBufferGetImageBuffer(self) {let ciImage = CIImage(cvPixelBuffer: buffer)let context = CIContext(options: nil)guard let cg = context.createCGImage(ciImage, from: ciImage.extent) else {return nil} return UIImage(cgImage: cg, scale: scale, orientation: orientation)}return nil}
2.回调中的数据处理
这边选用的方案是UIImageView来显示原始图像,并且在UIImageView上添加一个蒙层图像来显示识别框。此处选用蒙层的原因是,图像处理每帧需要70ms的处理时间,若直接显示处理后的图片会有延迟丢帧的情况视觉效果较差,因此实时图像采用原始图像数据,而识别框丢帧并不影响视觉效果。
//回调原始图像var videoCpatureCompletionBlock: ((UIImage) -> Void)?//回调CMSsmapleBuffer图像var videoCaptureCompletionBlockCMS: ((CMSampleBuffer)-> Void)?//回调蒙层图像var videoCaptureCompletionBlockMask: ((UIImage) -> Void)?//用于记录帧数var frameFlag : Int = 0//用于给异步线程加锁var lockFlagBool : Bool = falsefunc captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {if let image = sampleBuffer.imageWithCGImage(orientation: .up, scale: 1.0){self.frameFlag = self.frameFlag + 1var output = imageif(self.frameFlag != -1){self.videoCaptureCompletionBlockCMS?(sampleBuffer)self.videoCpatureCompletionBlock?(output)if(self.lockFlagBool == false){//此处必须开线程处理,否则会报错DispatchQueue.global().async {lockFlagBool = truevar output = image//addimageProcess为opencv图像处理过程,写在Objecj-C++文件中,本文后面记录output = opencv_test.addimageProcess(output)self.videoCaptureCompletionBlockMask?(output)lockFlagBool = false}}}else{print("丢帧")self.frameFlag = 0}}}
3.Mat数据转换为UIImage数据用于显示
为了最后能用于显示,还要转换为UImage,该部分很简单,直接调用OpenCV的库函数,当然如果想转换为CMSampleBuffer的话还需要重新添加丢失的数据,比如时间戳。
MatToUIImage()
参考地址:https://stackoverflow.com/questions/15726761/make-an-uiimage-from-a-cmsamplebuffer
四、Swift界面搭建
1.在UI层捕获相机数据
UI界面的操作比较简单,实例化之前的CameraController类,并设定configureCameraController函数来调用类中的prepare函数,以及接受回调的图像数据,这些回调对UIImageView的图像刷新必须要在主线程中,否则会报错。其中,selfImageView和maskImageView是两个自己创建的UImageView来显示UIImage图像的,这两个UIImageView要保持在同样位置同样大小。
let cameraController = CameraController()override func viewDidLoad() {configureCameraController() }func configureCameraController() {cameraController.prepare {(error) inif let error = error {print(error)}self.cameraController.videoCpatureCompletionBlock = { image inDispatchQueue.main.async {self.selfImageView.image = image}}self.cameraController.videoCaptureCompletionBlockMask = { image inDispatchQueue.main.async {self.maskImageView.image = image}}//直接显示CMSampleBuffer的方法// self.cameraController.videoCaptureCompletionBlockCMS = { CMSampleBuffer in//self.displayLayer.enqueue(CMSampleBuffer)//}}}
2.直接显示CMSampleBuffer方法
其实苹果的API也提供了直接显示CMSampleBuffer的简单方法,通过AVSampleBufferDisplayLayer以及其.enqueue方法,其展示方式如下:
var displayLayer:AVSampleBufferDisplayLayer!override func viewDidLoad() {displayLayer = AVSampleBufferDisplayLayer()displayLayer.videoGravity = .resizeAspect self.imageView.layer.addSublayer(displayLayer)self.displayLayer.frame.origin.y = self.imageView.frame.origin.yself.displayLayer.frame.origin.x = self.imageView.frame.origin.x}func configureCameraController() {cameraController.prepare {(error) inif let error = error {print(error)}//直接显示CMSampleBuffer的方法self.cameraController.videoCaptureCompletionBlockCMS = { CMSampleBuffer inself.displayLayer.enqueue(CMSampleBuffer)}}}
五、基于Object-C++的OpenCV图像处理部分
1.引入头文件
这部分用C++编写过OpenCV的都相当熟悉了,在.mm文件中引入以下头文件,并引入命名空间,若该部分找不到文件应当确认是否已正确安装OpenCV库。
#import <opencv2/opencv.hpp>
#import "opencv-test.h"
#import <opencv2/imgcodecs/ios.h>//对iOS支持
#import <opencv2/imgcodecs/ios.h>
//导入矩阵帮助类
#import <opencv2/highgui.hpp>
#import <opencv2/core/types.hpp>
#import <iostream>using namespace std;
using namespace cv;@implementation opencv_test//各类处理函数
@end
2.OpenCV人脸识别输出识别框
本文使用了OpenCV自带的人脸识别框架CascadeClassifier,将得到的人脸坐标放入vector中,最后绘制在蒙层上,最后输出蒙层图片。其它对于图像的处理也可以用相同的方式处理,在参考资料中有马赛克操作。
+(UIImage*)addimageProcess:(UIImage*)image {//用于记录时间CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();Mat src;//将iOS图片->OpenCV图片(Mat矩阵)UIImageToMat(image, src);Mat src_gray;//图像灰度化cvtColor(src, src_gray, COLOR_RGBA2GRAY, 1);std::vector<cv::Rect> faces;//初始化OpenCV的人脸识别检测器CascadeClassifier faceDetector;//获取权重文件,文件需要提前导入至工程目录中NSString* cascadePath = [[NSBundle mainBundle]pathForResource:@"haarcascade_frontalface_alt"ofType:@"xml"];//配置检测器faceDetector.load([cascadePath UTF8String]);faceDetector.detectMultiScale(src_gray, faces, 1.1,2, 0|CASCADE_SCALE_IMAGE, cv::Size(30, 30));//确定图像宽高int width = src.cols;int height = src.rows;//Mat Mask = Mat::zeros(width, height, CV_8UC4);//创建透明蒙层图像 Scalar(0,0,0,0) 分别是RGBA A为透明度Mat Mask = Mat(height, width, CV_8UC4, Scalar(0,0,0,0));// Draw all detected facesfor(unsigned int i = 0; i < faces.size(); i++){const cv::Rect& face = faces[i];// Get top-left and bottom-right corner pointscv::Point tl(face.x, face.y);cv::Point br = tl + cv::Point(face.width, face.height);// Draw rectangle around the faceScalar magenta = Scalar(0, 255, 0, 255);cv::rectangle(Mask, tl, br, magenta, 4, 8, 0);}//打印处理时间CFAbsoluteTime endTime = (CFAbsoluteTimeGetCurrent() - startTime);NSLog(@"normalProcess方法耗时: %f ms", endTime * 1000.0);return MatToUIImage(Mask);
}
参考资料:https://www.twblogs.net/a/5b830b452b717766a1eadb20/?lang=zh-cn
总结
遇到的困难:一是在于方案中用UIImageView来进行显示,必须在主线程中进行渲染,对于线程的处理相对繁琐,若是处理不得当便会有延时丢帧不刷新等的问题。
存在的问题:OpenCV自带的人脸识别算法比较老旧,处理速度也比较慢效果也一般,要引入其他神经网络框架在客户端上的可行性有待讨论,处理速度也未知。
另外,若有需要总的工程文件的可以私聊我。
相关文章:

如何用iOS自带摄像头进行拍摄获取视频流以及OpenCV图像处理实时显示
目录概述一、如何用Swift调用OpenCV库1.项目引入OpenCV库2.桥接OpenCV及Swift二、运用AVFoundation获取实时图像数据1.建立视频流数据捕获框架2.建立 Capture Session3.取得并配置 Capture Devices4.设定 Device Inputs5.配置Video Data Output输出6.工程隐私权限配置7.处理相机…...

智安网络|为什么说防火墙是我们信息安全的第一道防线?
网络安全现状: ①攻击者需要的技术水平逐渐降低,手段更加灵活,联合攻击急你的剧增多:网络蠕虫具有隐蔽性、传染性、破坏性、自主攻击能力,新一代网络蠕虫和黑客攻击、计算机病毒之间的界限越来越模糊 ②网络攻击趋利…...

Android多媒体功能开发(8)——使用VideoView控件播放视频
Android播放视频类主要有两种方式: VideoView控件SurfaceView控件MediaPlayer VideoView是SurfaceView的子类,实际上VideoView相当于SurfaceView MediaPlayer。SurfaceView支持的功能VideoView都支持。也可用VideoViewMediaPlayer的方式播放。 视频播放…...
python调用CC++
python调用C程序 一般来说在python调用C/C程序主要可以分为3步: 1、编写C/C实现程序。2、将C/C程序编译成动态库。-3、在Python中调用编译生成的库。Python在调用C/C程序时有一些不同,需要注意。 Python调用C语言程序比较简单,将C语言程序…...

[golang gin框架] 10.Gin 商城项目介绍
一.商城项目介绍 1.详细功能介绍图 2.数据库 ER 图 需要用到的数据表举例 二.MVC架构搭建以及执行流程分析 1.关于 MVC 模式的简单介绍 Gin 不是一个 MVC 的框架,所有的代码都可以写在 main.go 中。当我们的项目比较大的时候, 所有代码写在一个文件里面…...

Endor Labs:2023年十大开源安全风险
近日,Endor Labs发布了一份新报告,确定了2023年的十大开源安全风险。报告显示,许多软件公司依赖于开源软件代码,但在如何衡量和处理与开源软件相关的风险和漏洞方面缺乏一致性。调查发现,在应用程序中超过80%的代码可能…...
关于Error和Exception的一些思考 小结
目录 1. ERROR 2. Exception 2.1 checked Exception 2.2 unchecked Exception 2.3 区别 3. 内存溢出 3.1 堆溢出 3.2 永久代/元空间溢出 3.3 方法栈溢出 Java中,所有的异常都有一个共同的父类:Throwable类。 Throwable类有两个重要的子类&#…...

Mac环境变量配置(Java)
1.打开终端: 2.输入命令:【/usr/libexec/java_home -V】,查看默认的jdk下载地址(绿色下划线的就是jdk默认路径)(注意⚠️:命令行终端是区分大小写的【-v 是不对的,必须是大写 -V】) …...

通过这三个文件彻底搞懂rocketmq的存储原理
前言 RocketMQ是阿里开发的一个高性能的消息队列,支持各种消息类型,而且支持事务消息,可以说是现在的很多系统中的香饽饽了,所以呢,怎么使用大家肯定是要学习的 我们作为一个有梦想的程序员,在学习一门技…...

Linux安装Nvidia显卡驱动
使用的Linux系统为 Ubuntu 18.04,显卡为GeForce RTX 3060 。 查看ubuntu版本号命令:sudo lsb_release -a 查看显卡型号命令:lspci | grep -i vga (详细查看方法: 查看显卡型号)。 下面是安装显卡驱动步…...

GPT-4 介绍
1 简介 本文根据openAI的2023年3月的《GPT-4 Technical Report 》翻译总结的。 原文地址:https://arxiv.org/pdf/2303.08774.pdf 原文确实没有GPT-4 具体的模型结构,openAI向盈利组织、非公开方向发展了。也没透露硬件、训练成本、训练数据、训练方法等…...
Ubuntu下单机安装Hadoop详细教程(附所需安装包下载)
目录 前言 一、创建Hadoop用户 二、更新apt和安装Vim编辑器 三、安装SSH和配置SSH无密码登录 四、安装Java环境 1. 安装JDK 2. 配置JDK环境 3. 检验安装 五、安装单机Hadoop 1. 下载安装Hadoop 2. 运行示例 总结 前言 本文安装的 Hadoop 及 Java 环境基于林子雨老…...

【嵌入式烧录/刷写文件】-2.1-详解Intel Hex格式文件
目录 1 什么是Intel Hex 2 Intel Hex的格式 2.1 Intel Hex的Record结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminators文本行终止符 3 Hex文件的…...

【云原生】初识 Kubernetes — pod 的前世今生
目录标题前言🐳 Kubernetes到底是什么?🐬 K8s 的由来🐬K8s 的工作方式🐬 K8s 主要组件🐋Master 组件🐋Node 组件🐳 pod 是什么?🐬pod 的概念🐬控制…...
【基础篇】Java类加载器详解
类加载过程详解 类的生命周期 类从被加载到虚拟机内存到开始卸载出内存为止,生命周期可以简单概括为7个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析ÿ…...

Pytorch动手实现Transformer机器翻译
Pytorch动手实现Transformer机器翻译前言一、环境配置1. torchtextMethod1:Method2:2. Spacy以en包下载为例:手动安装语言包到spacy3. NLTKMethod1:Method2:二、运行结果1. 模型训练(train)2. 翻…...

宝塔面板部署node+vue项目注意事项
宝塔面板部署nodevue项目注意事项 宝塔连接云服务器 如果服务器上没有安装宝塔面板,需要先安装,安装流程如下: 从宝塔官网主页进去,点击下载安装,然后点击在线安装 输入服务器IP和密码在服务器上安装宝塔面板 等待一…...

【LeetCode】剑指 Offer 39. 数组中出现次数超过一半的数字 p205 -- Java Version
题目链接:https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/ 1. 题目介绍(39. 数组中出现次数超过一半的数字) 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可…...

fisco bcos用caliper0.2.0进行压力测试的安装配置
一、前期环境 1. 硬件 需要外网权限 2. 操作系统 版本要求:Ubuntu > 16.04, CentOS > 7, MacOS > 10.14 3. 基础软件 python 2.7,make,g,gcc,git sudo apt install python2.7 make g gcc git curl git confi…...

正在进行 | 用友企业数智化财务峰会落地广州 高能不断
3月28日,以「智能会计 价值财务」为主题的“2023企业数智化财务创新峰会”登陆广州。 此次用友企业数智化财务创新峰会,邀请了知名院校的专家学者、央国企等大型企业财务数智化领路人以及羊城权威媒体,近千人相约广州越秀国际会议中心,深度聚焦大型企业财务数智化创新应用…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...

Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...