View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

更多视频

大多数浏览器和
Developer App 均支持流媒体播放。

  • 概览
  • 转写文稿
  • 代码
  • 探索计算机视觉 API

    学习如何将计算机视觉只能添加到你的 app,将 Core Image,Vision 与 Core ML 的力量结合起来。超越单纯的机器学习技术,对图片与视频产生更深层次的理解。探索 Core Image 与 Vision 的全新 API,以类似 Contour Detection 与 Optical Flow 的方式,将计算机视觉以全新阈值过滤器的方式加入你的 app。 要了解更多关于相关基础框架的内容,请查看“视觉框架:以 Core ML 为基础”与“Core Image:性能,原型开发与 Python”。若要进一步探索计算机视觉 API,请查看[“在 Vision 中检测身体与手部姿势”]与[“探索 Action 与 Vision app”]等节。

    资源

    • Vision
      • 高清视频
      • 标清视频

    相关视频

    WWDC21

    • 用 Vision 提取文稿数据

    WWDC20

    • 使用 Vision 侦测身体与手部位姿
    • 探索 Action & Vision app

    WWDC19

    • 计算机视觉框架中的文本识别
    • Understanding Images in Vision Framework
  • 下载

    (你好 WWDC 2020) 你好 欢迎参加 WWDC

    欢迎参加 WWDC 我叫 Frank Doepke 跟我的同事 David Hayward 一起 我们将探索计算机视觉 API

    为什么要谈论计算机视觉? 计算机视觉可以增强你的 app 即便这不是你的业务核心 它会为你的 app 带来一些新东西

    让我给你举一个例子

    银行 app 使你能兑现支票 它们利用计算机视觉让相机为你读取支票 因此你不必再输入信息 显然 计算机视觉不是银行业的核心 但通过这样做 你可以为用户省去很多步骤 他们不必再输入任何东西

    另一件事可能是 你想读取二维码 或者读收据 所有这些可能都不是你的 app 的核心 但使用相机使你的用户 能更轻松地做到这一点

    我们有什么 API 可以用于 计算机视觉呢?

    在最高层 我们有 VisionKit 它是 VNDocumentCamera 的所在地 你可能在备忘录或信息或邮件中 看到过它扫描文档 然后我们用 Core Image 对图像进行图像处理 用计算机视觉分析图像 最后 用 Core ML 进行机器学习推理 今天我们只关注 Core Image 和计算机视觉 但我想确保你不要将它们视为 并排而立的柱子 它们实际上可以紧密相连 我可能想进行一些图像预处理 将其在计算机视觉中运行 从那里获取结果 将其导入 Core ML 或返回到 Core Image 以创建一些效果 要谈论如何用 Core Image 为计算机视觉预处理图像 我想交给我的同事 David Hayward

    谢谢 Frank 我想借此机会介绍一下 如何用 Core Image 改进计算机视觉算法

    如果你对 Core Image 不熟悉 它是以 Metal 为基础的 优化型易用图像处理框架 要深入了解它的工作原理 我建议你观看 WWDC 2017 专题介绍

    你的 app 为什么应该使用 Core Image 和计算机视觉有两个主要原因

    用 Core Image 对计算机视觉输入 进行预处理 可以使算法更快、更稳健 (使你的图像更易于分析) 用 Core Image 对计算机视觉输出 进行后处理 可以为你的 app 提供新的方式 来向用户显示这些结果 (使分析结果可视化) 同时 Core Image 是为机器学习训练 进行增强的绝佳工具 在 WWDC 2018 的介绍中 有一些很好的例子 (CORE IMAGE:性能、原型和 PYTHON) (缩放) 准备要分析的图像的最佳方法之一是 缩小图像以获得最佳性能

    整体质量最好的缩放器是 CILanczosScale

    在代码中使用这个滤镜非常容易 只需要导入 CIFilterBuiltins 标头 创建滤镜实例 设置输入属性 然后获取 outputImage 就这么简单

    但这只是 Core Image 中的 几个重采样滤镜之一 取决于你的算法 可能最好使用线性插值 CIAffineTransform

    形态学操作是一种很好的技术 可以使图像中的小特征更加突出

    用 CIMorphologyRectangleMaximum 进行膨胀 会使图像的较亮区域变大

    用 CIMorphologyRectangleMinimum 进行腐蚀 会使这些区域变小

    更好的方法是用 CIMorphologyRectangleMinimum 然后是 CIMorphology- RectangleMaximum 进行关闭 这对去除图像中可能影响算法的 小区域噪声非常有用

    有些算法只需要单色输入 在这种情况下 计算机视觉会自动将 RGB 转换成灰度 如果你有关于输入图像的领域知识 那么用 Core Image 转换成灰色的结果可能会更好

    通过 CIColorMatrix 你可以为 本转换指定任何加权

    或者通过 CIMaximumComponent 将使用信号最好的信道

    在进行图像分析之前降噪也值得考虑

    通过几次 CIMedianFilter 可在不软化边缘的情况下降噪

    CIGaussianBlur 和 CIBoxBlur 也是快速降噪的方法

    也考虑使用 CINoiseReduction 滤镜

    Core Image 还有各种边缘检测滤镜

    对于 Sobel 边缘检测器 可以使用 CIConvolution3X3

    更好的方法是使用 CIGaborGradients 这会产生一个 2D 梯度向量 也更能容忍噪声

    增强图像的对比度可能有助于对象检测

    CIColorPolynomial 使你能指定 任意三阶对比度函数 CIColorControls 提供线性对比度参数

    今年 Core Image 也有一些新的滤镜 可以将你的图像转换成黑白色

    例如 CIColorThreshold 使你能在 app 代码中设置阈值 而 CIColorThresholdOtsu 会根据图像的直方图自动确定最佳阈值

    Core Image 还有 用于比较两个图像的滤镜 这有助于为检测视频帧间运动做好准备

    例如 CIColorAbsoluteDifference 是今年的新滤镜 对这有帮助

    此外 CILabDeltaE 会用 旨在匹配人类对颜色的感知 的公式来比较两个图像

    这些只是 Core Image 中 200 多个内置滤镜中的一些

    为了帮助你使用这些内置滤镜 本说明文档包括参数描述 示例图像 甚至示例代码

    如果这些过滤器都不满足你的需求 那么你可以用 Metal Core Image 轻松编写自己的滤镜 我们建议你参阅我们今年发布的 关于这个问题的课程

    对于图像处理和计算机视觉 务必注意图像 可能有各种彩色空间

    你的 app 可能会收到从传统 sRGB 到宽色域 P3 空间中的图像 现在甚至支持 HDR 彩色空间

    你的 app 应该为各种彩色空间做好准备 好消息是 Core Image 使这非常容易

    Core Image 自动将输入 转换到其工作空间 Unclamped、Linear、BT.709 primaries

    但你的算法可能需要不同彩色空间的图像 在这种情况下 你应该执行以下操作

    你需要从 CGColorSpace 获取 要使用的彩色空间的变量 你会调用 imagematchFromWorkingSpace

    在该空间应用算法 然后调用 imagematchtoWorkingSpace

    这样就可以了 我今天的最后一个话题是用 Core Image 对计算机视觉输出进行后处理 这方面的一个例子是用 Core Image 从计算机视觉条码 observation 重新生成条码图像

    你只需要在代码中创建滤镜实例

    将其 barcodeDescriptor 属性设置为 计算机视觉 observation 的属性 最后获得输出图像 结果如下

    同样 你的 app 可以基于计算机视觉 face observation 应用滤镜

    举例来说 你可以用这种方法 轻松使用晕影效果

    代码其实非常简单 需要注意的一件事是 你需要从计算机视觉的标准化坐标系 转换为 Core Image 的笛卡尔坐标系

    创建晕影滤镜后 你就可以用 compostingover 在图像上覆盖晕影

    你还可以用 Core Image 使向量场可视化 Frank 稍后会进行演示

    我的介绍到此结束 下面由 Frank 进一步谈论计算机视觉

    好的 谢谢 David 现在我要谈谈如何通过 使用计算机视觉来理解图像

    我们有任务、机器和结果 任务是你想做的事 机器是执行任务的工具 结果当然就是你想要的 你想得到的

    任务可能在我们的编译程序 VNRequests 中 例如 VNDetectFaceRectanglesRequest 机器是这两个中的一个 我们有 ImageRequestHandler 或 SequenceRequestHandler

    我们得到的结果被称为 VNObservation 这取决于你执行的任务 例如用于检测矩形的 VNRectangleObservation

    我们首先执行 ImageRequestHandler 上的请求 我们从那里获得 observation 让我们看一个具体的例子

    我们想读取文本 因此我们使用 VNRecognizeTextRequest

    然后我用图像创建 ImageRequestHandler

    从中我得到了 observation 只是纯文本

    计算机视觉在 2020 年有什么新功能?

    首先 我们有手势和位姿 要了解更多 请参阅“手势和位姿”会议

    然后你可能看过我们的轨迹检测 关于这一点 你可以在 “探索动作与计算机视觉 app” 中查看更多信息

    今天 我们要关注轮廓检测和光流

    什么是轮廓检测? 通过轮廓检测 我可以找到图像的边缘

    正如我们在这里看到的 红线现在显示 我们在这张图形中找到的轮廓

    我们从图像开始 然后创建 VNDetectContourRequest

    我们现在可以设置图像的对比度 以进行增强 例如 一些对比度如何突显 我们可以在两者之间切换 我们想用这个浅色背景 在深色背景下运行吗? 这样可以区分前景和背景?

    最后 我们可以添加最大图像尺寸 这使你能权衡性能和精确性

    这意味着 例如 如果你以较低的分辨率看它 你仍然可以看到轮廓 但轮廓可能不会精确地沿着边缘 但运行得更快 因为它能以较低的分辨率运行 相比之下 当我们使用较高的分辨率时 在进行一些后处理时你可能需要这样做 我们会得到更精确的轮廓 但这需要更长的时间 因为需要做更多工作

    让我们看看我们得到的 observation

    这里我们有一个非常简单的图像 两个正方形 里面有一个圆形

    我们得到了 VNContoursObservation

    topLevelContours 是我们看到的两个矩形

    里面是 childContour 它们是嵌套的 这些是圆形

    然后我们得到 contourCount 我可以用来穿过所有轮廓

    但使用诸如索引路径要容易得多 你可以看到 它们相互嵌套 我现在可以遍历图形

    最后 我还得到 normalizedPath 这是一个 CGPath 可以轻松地用于渲染

    现在 什么是 VNContour? 在例子中 我们得到了 VNContour

    这是最外部的轮廓 我们的父轮廓 嵌套在里面的是 childContour 这些是内部轮廓

    我的轮廓有索引路径 当然 每个 childContour 都有索引路径 我可以用来再次遍历图形

    然后我得到 pointCount 中的 normalizedPoint 这是轮廓最重要的部分 因为它描述我们找到的每个线段 因为我们不仅找到了像素 我们得到了一个轮廓 一条路径

    我们还有一个 aspectRatio 我会在下一张幻灯片上 讨论这个问题

    然后我们有要渲染的 normalizedPath 在处理轮廓时 我们需要记住几点 让我们来看看这张图像

    它是1920x1080 像素 中间有一个高和宽均为 1080 像素的圆形

    但计算机视觉使用标准化坐标空间 因此我们的图像是高 1.0 宽 1.0 因此 圆形的高度现在是 1.0 但宽度是 0.5625 如果你想考虑到 你检测到的形状的几何结构 你需要看看计算它的原始图像的 aspectRatio

    在能对其进行分析时 轮廓会很有趣 而且我们有几个实用程序可供你使用

    VNGeometryUtils 提供一些 API 例如 我们有 boundingCircle 它是完全封装你检测到的轮廓的 最小的圆形 很适合用来比较轮廓

    然后我们可以计算面积 而且我们可以计算周长

    接下来你需要做的是 简化轮廓 当我们从图像得到轮廓时 它们往往有噪声 让我们看看这个例子

    我们有一个拍摄的矩形

    但你可以看到 它有一些小弯 轮廓是沿着这些小弯的 现在我没有边角的所有点 甚至没有中间的所有点

    我现在可以通过用 Epsilon 使用多边形的近似值 Epsilon 意味着我可以滤除 边缘周围有噪声的小区域 这样只有分明的轮廓边缘才会保留

    现在 我再次得到一个完美的矩形 我只有四个点 因此如果我需要分析形状 这对我来说非常简单 因为我可以说 “如果它有四个点 那么它是四边形” 并且我检测到它是什么形状

    我们来看一个具体的例子 说明如何使用所有这些 (计算机视觉问题到原生解决方案)

    假设我们需要通过 恢复在穿孔卡片机上完成的 非常老的计算机代码来拯救世界

    我们的任务是识别穿孔卡片上的小坑 因为没人有穿孔卡片阅读器了

    我们在网上搜索 找到关于如何做到这一点的 计算机视觉博客帖子 但它是用 Python 编写的 我们的任务当然是 将其以原生状态带到我们的平台上 这样我们就能以最好的方式运行它

    现在我们有一段 Python 代码 如果你不懂 Python 别担心 我会简要地为你介绍一下 理念通常都是一样的 我们先进行一些图像处理

    然后进行一些图像分析

    得到一些需要可视化的结果 即便你不理解 Python 我想在一开始就向你强调一点 你看到的前三行 可以看到 我们需要导入几个库 Python 没有库 这些是你需要包含的第三方库

    我们如何原生地做到这一点?

    对于图像处理部分 我们需要加载图像 你已经知道该怎么做了 使用 CGImageSource 从中获取一个用户界面图像 将其加载到 CIImage… 给它命名 然后你可以通过使用 CIFilter 来用 Core Image 处理图像 就像在 CIAbsoluteThreshold 中一样 或者是 David 前面说过的许多其它方法

    现在我们要进行图像分析 为此 我们从刚刚处理的 CIImage 创建 ImageRequestHandler 然后我们执行请求 例如 VNDetectContourRequest 这个请求的好处是 我们甚至不必预处理图像

    然后我们使结果可视化 同样 我们可以用 Core Image 来做到这一点 这使我们能将其直接叠加到我们拥有的 在同一背景下的图像 你可以使用 CIMeshGenerator 或 CITextGenerator

    但我也可以使用 CoreGraphics 或 UIKit 将其渲染到图像上方的层 好的 看过所有这些幻灯片之后 让我们看一个真正的演示 让我转到我的演示设备

    我准备的是一个小“游乐场” 你看到我已经加载了图像

    我创建了 contourRequest…

    然后我执行它 然后就好了 我可以看到所有轮廓 包括我在找的小坑 注意 我找到了 387 个轮廓 这可能比我想要的多

    我们需要滤除所有这些轮廓

    我做了一些准备 而且我隐藏了一些代码 让我揭示这段代码 以及所有的一切… 由于我的领域知识 我知道轮廓在一个蓝色背景上 现在 用一些 CIFiltering 先将所有噪声模糊

    然后用颜色控件来显示对比度 然后用轮廓探测 运行过滤后的图像 现在你看到 我只找到 32 个轮廓 这是我起初在意的小坑 好的 让我们回到幻灯片

    通常 我会在演示中谈论我做了什么 但实际上 更重要的是我不必做什么

    你注意到了 我没有加载任何第三方软件包 因为这都是 OS 的一部分 我只使用了 UIKit Core Image 和计算机视觉

    通过使用最佳处理路径 我也从来没离开图像管道 因为我停留在管道内

    没有将图像转换成矩阵 由此 我节省了内存 也节省了大量计算成本

    这是轮廓检测 接下来 我们来看看光流 什么是光流?

    我们要分析两个帧之间的运动

    传统上 我们可能会使用注册 这在很长一段时间内 是计算机视觉的一部分 它使整张图像对齐 让我们看一个例子

    我们有这两个点 我们来看看这是不是用相机拍的照片 然后我们移动了相机

    现在 这两个点移到了顶部和右侧

    通过告诉我图像向上和向右移动了多少 注册会使我获得两个图像之间的对齐情况

    而光流是不同的 它给出 X 和 Y 之间每个像素的移动 这是计算机视觉今年的新功能

    在例子中 我们同样有两个点

    但现在 它们分开了

    图像注册不能准确地获取这一信息 但是我可以使用光流 因为它会告诉我 对于每个像素 它们是如何移动的

    让我们看看光流的结果

    从光流 我得到了 VNPixelBufferObservation 它是一个浮点图像 有交替的 X 和 Y 运动

    当我们有这样一个视频时 你可以想象 或许仅仅看这些值本身 会很难想象发生了什么 因为它们是要在以后的算法中处理的 但是如果我想看看 我可以用 Core Image 使结果可视化 正如之前 David 在课程中提到的 这是有办法实现的 我们创建了一个小的自定义内核 现在 你可以看到一切是如何移动的 我有一个彩色编码 它显示运动的强度 小三角形显示运动的方向

    让我简要地告诉你我们是怎么做到的 我们编写一个自定义滤镜 我需要加载内核 我们会在幻灯片附件中将其提供给你 然后我只需要用所需箭头尺寸的参数 应用该内核 并将其作为滤镜运行 现在 在我的计算机视觉代码中 我只需要 运行 VNGenerateOpticalFlow 请求 我得到 pixelBuffer 的 observation 我可以将其封装到 CIImage 中 然后只需要将其导入滤镜 并获取输出图像

    让我们总结一下今天谈论的东西 计算机视觉并不难 而且它能增强你的 app 我们的原生 API 使其易于快速采用 通过将这些结合起来 你可以创建一些有趣的东西 我期待着你打造出绝佳的 app 和伟大创新 感谢你参加我们的会议 尽情享受 WWDC 的剩余部分

    • 19:24 - Reading punchcards playgrounds

      import UIKit
      import CoreImage
      import CoreImage.CIFilterBuiltins
      import Vision
      
      
      public func drawContours(contoursObservation: VNContoursObservation, sourceImage: CGImage) -> UIImage {
      	let size = CGSize(width: sourceImage.width, height: sourceImage.height)
      	let renderer = UIGraphicsImageRenderer(size: size)
      	
      	let renderedImage = renderer.image { (context) in 
      		
      		let renderingContext = context.cgContext
      		
          // flip the context
          let flipVertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
          renderingContext.concatenate(flipVertical)
              
      		// draw the original image
      		renderingContext.draw(sourceImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
      		
      		renderingContext.scaleBy(x: size.width, y: size.height)
      		renderingContext.setLineWidth(3.0 / CGFloat(size.width))
      		let redUIColor = UIColor.red
      		renderingContext.setStrokeColor(redUIColor.cgColor)
      		renderingContext.addPath(contoursObservation.normalizedPath)
      		renderingContext.strokePath()
      	}
      	
      	return renderedImage;
      }
      
      let context = CIContext()
      if let sourceImage = UIImage.init(named: "punchCard.jpg")
      {
      	var inputImage = CIImage.init(cgImage: sourceImage.cgImage!)
      	
      	let contourRequest = VNDetectContoursRequest.init()
          
      // Uncomment the follwing section to preprocess the image
      //	do {
      //			let noiseReductionFilter = CIFilter.gaussianBlur()
      //			noiseReductionFilter.radius = 1.5
      //			noiseReductionFilter.inputImage = inputImage
      //
      //			let monochromeFilter = CIFilter.colorControls()
      //			monochromeFilter.inputImage = noiseReductionFilter.outputImage!
      //			monochromeFilter.contrast = 20.0
      //			monochromeFilter.brightness = 8
      //			monochromeFilter.saturation = 50
      //
      //			let filteredImage = monochromeFilter.outputImage!
      //
      //			inputImage = filteredImage
      //		}
      	
      	let requestHandler = VNImageRequestHandler.init(ciImage: inputImage, options: [:])
      
      	try requestHandler.perform([contourRequest])
      	let contoursObservation = contourRequest.results?.first as! VNContoursObservation
      	print(contoursObservation.contourCount)
      	_ = drawContours(contoursObservation: contoursObservation, sourceImage: sourceImage.cgImage!)
      } else {
      	print("could not load image")
      }
    • 23:05 - Optical Flow Visualizer (CI kernel)

      //
      //  OpticalFlowVisualizer.cikernel
      //  SampleVideoCompositionWithCIFilter
      //
      
      
      kernel vec4 flowView2(sampler image, float minLen, float maxLen, float size, float tipAngle)
      {
      	/// Determine the color by calculating the angle from the .xy vector
      	///
      	vec4 s = sample(image, samplerCoord(image));
      	vec2 vector = s.rg - 0.5;
      	float len = length(vector);
      	float H = atan(vector.y,vector.x);
      	// convert hue to a RGB color
      	H *= 3.0/3.1415926; // now range [3,3)
      	float i = floor(H);
      	float f = H-i;
      	float a = f;
      	float d = 1.0 - a;
      	vec4 c;
      		 if (H<-3.0) c = vec4(0, 1, 1, 1);
      	else if (H<-2.0) c = vec4(0, d, 1, 1);
      	else if (H<-1.0) c = vec4(a, 0, 1, 1);
      	else if (H<0.0)  c = vec4(1, 0, d, 1);
      	else if (H<1.0)  c = vec4(1, a, 0, 1);
      	else if (H<2.0)  c = vec4(d, 1, 0, 1);
      	else if (H<3.0)  c = vec4(0, 1, a, 1);
      	else             c = vec4(0, 1, 1, 1);
      	// make the color darker if the .xy vector is shorter
      	c.rgb *= clamp((len-minLen)/(maxLen-minLen), 0.0,1.0);
      	/// Add arrow shapes based on the angle from the .xy vector
      	///
      	float tipAngleRadians = tipAngle * 3.1415/180.0;
      	vec2 dc = destCoord(); // current coordinate
      	vec2 dcm = floor((dc/size)+0.5)*size; // cell center coordinate
      	vec2 delta = dcm - dc; // coordinate relative to center of cell
      	// sample the .xy vector from the center of each cell
      	vec4 sm = sample(image, samplerTransform(image, dcm));
      	vector = sm.rg - 0.5;
      	len = length(vector);
      	H = atan(vector.y,vector.x);
      	float rotx, k, sideOffset, sideAngle;
      	// these are the three sides of the arrow
      	rotx = delta.x*cos(H) - delta.y*sin(H);
      	sideOffset = size*0.5*cos(tipAngleRadians);
      	k = 1.0 - clamp(rotx-sideOffset, 0.0, 1.0);
      	c.rgb *= k;
      	sideAngle = (3.14159 - tipAngleRadians)/2.0;
      	sideOffset = 0.5 * sin(tipAngleRadians / 2.0);
      	rotx = delta.x*cos(H-sideAngle) - delta.y*sin(H-sideAngle);
      	k = clamp(rotx+size*sideOffset, 0.0, 1.0);
      	c.rgb *= k;
      	rotx = delta.x*cos(H+sideAngle) - delta.y*sin(H+sideAngle);
      	k = clamp(rotx+ size*sideOffset, 0.0, 1.0);
      	c.rgb *= k;
      	/// return the color premultiplied
      	c *= s.a;
      	return c;
      }
    • 23:26 - Optical Flow Visualizer (CIFilter code)

      class OpticalFlowVisualizerFilter: CIFilter {
      	var inputImage: CIImage?
      	
      	let callback: CIKernelROICallback = {
      			(index, rect) in
      				return rect
      			}
      	
      	static var kernel: CIKernel = { () -> CIKernel in
      		let url = Bundle.main.url(forResource: "OpticalFlowVisualizer",
      								  withExtension: "ci.metallib")!
      		let data = try! Data(contentsOf: url)
      		
      		return try! CIKernel(functionName: "flowView2",
      								  fromMetalLibraryData: data)
      	}()
      
      	override var outputImage : CIImage? {
      		get {
      			guard let input = inputImage else {return nil}
      			return OpticalFlowVisualizerFilter.kernel.apply(extent: input.extent, roiCallback: callback, arguments: [input, 0.0, 100.0, 10.0, 30.0])
      		}
      	}
      }
    • 23:42 - Optical Flow Visualizer (Vision code)

      var requestHandler = VNSequenceRequestHandler()
                  var previousImage:CIImage?
      			if (self.previousImage == nil) 
      			{
      				self.previousImage = request.sourceImage
      			}
      			let visionRequest = VNGenerateOpticalFlowRequest(targetedCIImage: source, options: [:])
      			
      			do {
      				try self.requestHandler.perform([visionRequest], on: self.previousImage!)
      				if let pixelBufferObservation = visionRequest.results?.first as? VNPixelBufferObservation
      				{
      					source = CIImage(cvImageBuffer: pixelBufferObservation.pixelBuffer)
      				}
      			} catch {
      				print(error)
      			}
      			// store the previous image
      			self.previousImage = request.sourceImage
      			
      			let ciFilter = OpticalFlowVisualizerFilter()
      			ciFilter.inputImage = source
      			let output = ciFilter.outputImage
  • 正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。

    提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。

Developer Footer

  • 视频
  • WWDC20
  • 探索计算机视觉 API
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则