- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我们正在尝试规范化 UIImage
这样它就可以正确地传递到 CoreML 模型中。
我们从每个像素中检索 RGB 值的方法是首先初始化一个 [CGFloat]
名为 rawData
的数组每个像素的值,使得红色、绿色、蓝色和 alpha 值都有一个位置。在 bitmapInfo
,我们从原始 UIimage 本身获取原始像素值并进行处理。这用于填充 bitmapInfo
context
中的参数, 一个 CGContext
多变的。我们稍后会用到 context
变量为 draw
一个CGImage
稍后将转换标准化的 CGImage
回到UIImage
.
使用嵌套的 for 循环遍历 x
和 y
坐标,找到所有像素的所有颜色(通过 CGFloat
的原始数据数组找到)中的最小和最大像素颜色值。设置一个绑定(bind)变量来终止 for 循环,否则会出现超出范围的错误。
range
指示可能的 RGB 值的范围(即最大颜色值和最小颜色值之间的差异)。
使用等式对每个像素值进行归一化:
A = Image
curPixel = current pixel (R,G, B or Alpha)
NormalizedPixel = (curPixel-minPixel(A))/range
和一个类似设计的嵌套 for 循环从上面解析 rawData
的数组并根据此归一化修改每个像素的颜色。
我们的大部分代码来自:
我们使用 CGFloat
而不是 UInt8
因为归一化像素值应该是介于 0 和 1 之间的实数,而不是 0 或 1。
func normalize() -> UIImage?{
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let cgImage = cgImage else {
return nil
}
let width = Int(size.width)
let height = Int(size.height)
var rawData = [CGFloat](repeating: 0, count: width * height * 4)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bytesPerComponent = 8
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
let context = CGContext(data: &rawData,
width: width,
height: height,
bitsPerComponent: bytesPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo)
let drawingRect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
context?.draw(cgImage, in: drawingRect)
let bound = rawData.count
//find minimum and maximum
var minPixel: CGFloat = 1.0
var maxPixel: CGFloat = 0.0
for x in 0..<width {
for y in 0..<height {
let byteIndex = (bytesPerRow * x) + y * bytesPerPixel
if(byteIndex > bound - 4){
break
}
minPixel = min(CGFloat(rawData[byteIndex]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 1]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 2]), minPixel)
minPixel = min(CGFloat(rawData[byteIndex + 3]), minPixel)
maxPixel = max(CGFloat(rawData[byteIndex]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 1]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 2]), maxPixel)
maxPixel = max(CGFloat(rawData[byteIndex + 3]), maxPixel)
}
}
let range = maxPixel - minPixel
print("minPixel: \(minPixel)")
print("maxPixel : \(maxPixel)")
print("range: \(range)")
for x in 0..<width {
for y in 0..<height {
let byteIndex = (bytesPerRow * x) + y * bytesPerPixel
if(byteIndex > bound - 4){
break
}
rawData[byteIndex] = (CGFloat(rawData[byteIndex]) - minPixel) / range
rawData[byteIndex+1] = (CGFloat(rawData[byteIndex+1]) - minPixel) / range
rawData[byteIndex+2] = (CGFloat(rawData[byteIndex+2]) - minPixel) / range
rawData[byteIndex+3] = (CGFloat(rawData[byteIndex+3]) - minPixel) / range
}
}
let cgImage0 = context!.makeImage()
return UIImage.init(cgImage: cgImage0!)
}
在归一化之前,我们期望像素值范围为 0 - 255,在归一化之后,像素值范围为 0 - 1。
归一化公式能够将像素值归一化为 0 到 1 之间的值。但是当我们尝试打印(在遍历像素值时简单地添加打印语句)归一化之前的像素值以验证我们得到原始像素值正确,我们发现这些值的范围是关闭的。例如,一个像素值的值为 3.506e+305(大于 255)。我们认为我们一开始就弄错了原始像素值。
我们不熟悉 Swift 中的图像处理,我们不确定整个规范化过程是否正确。任何帮助将不胜感激!
最佳答案
一些观察:
你的 rawData
是 float ,CGFloat
,数组,但是你的上下文没有用 float 据填充它,而是用 UInt8
数据。如果您想要一个浮点缓冲区,请使用 CGBitmapInfo.floatComponents
构建一个浮点上下文并相应地调整上下文参数。例如:
func normalize() -> UIImage? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let cgImage = cgImage else {
return nil
}
let width = cgImage.width
let height = cgImage.height
var rawData = [Float](repeating: 0, count: width * height * 4)
let bytesPerPixel = 16
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 32
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
guard let context = CGContext(data: &rawData,
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo) else { return nil }
let drawingRect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
context.draw(cgImage, in: drawingRect)
var maxValue: Float = 0
var minValue: Float = 1
for pixel in 0 ..< width * height {
let baseOffset = pixel * 4
for offset in baseOffset ..< baseOffset + 3 {
let value = rawData[offset]
if value > maxValue { maxValue = value }
if value < minValue { minValue = value }
}
}
let range = maxValue - minValue
guard range > 0 else { return nil }
for pixel in 0 ..< width * height {
let baseOffset = pixel * 4
for offset in baseOffset ..< baseOffset + 3 {
rawData[offset] = (rawData[offset] - minValue) / range
}
}
return context.makeImage().map { UIImage(cgImage: $0, scale: scale, orientation: imageOrientation) }
}
但这引出了一个问题,即您为什么要为 float 据烦恼。如果您将此 float 据返回到您的 ML 模型,那么我可以想象它可能会有用,但您只是在创建一个新图像。因此,您还必须有机会只检索 UInt8
数据,进行浮点运算,然后更新 UInt8
缓冲区,并从中创建图像。因此:
func normalize() -> UIImage? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let cgImage = cgImage else {
return nil
}
let width = cgImage.width
let height = cgImage.height
var rawData = [UInt8](repeating: 0, count: width * height * 4)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 8
let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue
guard let context = CGContext(data: &rawData,
width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo) else { return nil }
let drawingRect = CGRect(origin: .zero, size: CGSize(width: width, height: height))
context.draw(cgImage, in: drawingRect)
var maxValue: UInt8 = 0
var minValue: UInt8 = 255
for pixel in 0 ..< width * height {
let baseOffset = pixel * 4
for offset in baseOffset ..< baseOffset + 3 {
let value = rawData[offset]
if value > maxValue { maxValue = value }
if value < minValue { minValue = value }
}
}
let range = Float(maxValue - minValue)
guard range > 0 else { return nil }
for pixel in 0 ..< width * height {
let baseOffset = pixel * 4
for offset in baseOffset ..< baseOffset + 3 {
rawData[offset] = UInt8(Float(rawData[offset] - minValue) / range * 255)
}
}
return context.makeImage().map { UIImage(cgImage: $0, scale: scale, orientation: imageOrientation) }
}
我只取决于您是否真的需要为您的 ML 模型使用这个浮点缓冲区(在这种情况下,您可能会在第一个示例中返回 float 组,而不是创建新图像)或者目标是否只是创建规范化的 UIImage
。
我对此进行了基准测试,它在 iPhone XS Max 上比浮点再现快一点,但占用内存的四分之一(例如,一张 2000×2000 像素的图像使用 UInt8
占用 16mb,但使用 Float
时为 64mb)。
最后,我要提一下 vImage具有高度优化的功能,vImageContrastStretch_ARGB8888
这与我们上面所做的非常相似。只需 import Accelerate
然后您就可以执行如下操作:
func normalize3() -> UIImage? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let cgImage = cgImage else { return nil }
var format = vImage_CGImageFormat(bitsPerComponent: UInt32(cgImage.bitsPerComponent),
bitsPerPixel: UInt32(cgImage.bitsPerPixel),
colorSpace: Unmanaged.passRetained(colorSpace),
bitmapInfo: cgImage.bitmapInfo,
version: 0,
decode: nil,
renderingIntent: cgImage.renderingIntent)
var source = vImage_Buffer()
var result = vImageBuffer_InitWithCGImage(
&source,
&format,
nil,
cgImage,
vImage_Flags(kvImageNoFlags))
guard result == kvImageNoError else { return nil }
defer { free(source.data) }
var destination = vImage_Buffer()
result = vImageBuffer_Init(
&destination,
vImagePixelCount(cgImage.height),
vImagePixelCount(cgImage.width),
32,
vImage_Flags(kvImageNoFlags))
guard result == kvImageNoError else { return nil }
result = vImageContrastStretch_ARGB8888(&source, &destination, vImage_Flags(kvImageNoFlags))
guard result == kvImageNoError else { return nil }
defer { free(destination.data) }
return vImageCreateCGImageFromBuffer(&destination, &format, nil, nil, vImage_Flags(kvImageNoFlags), nil).map {
UIImage(cgImage: $0.takeRetainedValue(), scale: scale, orientation: imageOrientation)
}
}
虽然这采用了稍微不同的算法,但它值得考虑,因为在我的基准测试中,在我的 iPhone XS Max 上它比浮点再现快 5 倍多。
一些不相关的观察:
您的代码片段也在规范化 alpha channel 。我不确定你是否愿意这样做。通常颜色和 alpha channel 是独立的。上面我假设你真的只想标准化颜色 channel 。如果你也想标准化 alpha channel ,那么你可能有一个单独的 alpha channel 值的最小-最大范围并单独处理。但是,使用与颜色 channel 相同的值范围对 alpha channel 进行归一化(反之亦然)没有多大意义。
我没有使用 UIImage
宽度和高度,而是使用 CGImage
中的值。这是重要的区别,以防您的图片的比例不是 1。
例如,如果范围已经是 0-255(即不需要标准化),您可能需要考虑提前退出。
关于swift - 如何在 Swift 中规范化 UIImage 的像素值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55433107/
有没有办法在 .swift 文件(编译成 .swift 模块)中声明函数,如下所示: 你好.swift func hello_world() { println("hello world")
我正在尝试使用 xmpp_messenger_ios 和 XMPPFramework 在 iOS 上执行 MUC 这是加入房间的代码。 func createOrJoinRoomOnXMPP()
我想在我的应用程序上创建一个 3D Touch 快捷方式,我已经完成了有关快捷方式本身的所有操作,它显示正确,带有文本和图标。 当我运行这个快捷方式时,我的应用程序崩溃了,因为 AppDelegate
我的代码如下: let assetTag = Expression("asset_tag") let query2 = mdm.select(mdm[assetTag],os, mac, lastRe
我的 swift 代码如下所示 Family.arrayTuple:[(String,String)]? = [] Family.arrayTupleStorage:String? Family.ar
这是我的 JSON,当我读取 ord 和 uniq 数据时出现错误 let response2 : [String: Any] = ["Response":["status":"SUCCESS","
我想将 swift 扩展文件移动到 swift 包中。但是,将文件移动到 swift 包后,我遇到了这种错误: "Type 'NSAttributedString' has no member 'ma
使用CocoaPods,我们可以设置以下配置: pod 'SourceModel', :configurations => ['Debug'] 有什么方法可以用 Swift Package Manag
我正在 Xcode 中开发一个 swift 项目。我将其称为主要项目。我大部分都在工作。我在日期选择器、日期范围和日期数学方面遇到了麻烦,因此我开始了另一个名为 StarEndDate 的项目,其中只
这是 ObjectiveC 代码: CCSprite *progress = [CCSprite spriteWithImageNamed:@"progress.png"]; mProgressBar
我正在创建一个命令行工具,在 Xcode 中使用 Swift。我想使用一个类似于 grunt 的配置文件确实如此,但我希望它是像 Swift 包管理器的 package.swift 文件那样的快速代码
我假设这意味着使用系统上安装的任何 swift 运行脚本:#!/usr/bin/swift 如何指定脚本适用的解释器版本? 最佳答案 Cato可用于此: #!/usr/bin/env cato 1.2
代码说完全没问题,没有错误,但是当我去运行模拟器的时候,会出现这样的字样: (Swift.LazyMapCollection (_base:[ ] 我正在尝试创建一个显示报价的报价应用。 这是导入
是否可以在运行 Swift(例如 Perfect、Vapor、Kitura 等)的服务器上使用 RealmSwift 并使用它来存储数据? (我正在考虑尝试将其作为另一种解决方案的替代方案,例如 no
我刚开始学习编程,正在尝试完成 Swift 编程书中的实验。 它要求““编写一个函数,通过比较两个 Rank 值的原始值来比较它们。” enum Rank: Int { case Ace = 1 ca
在您将此问题标记为重复之前,我检查了 this question 它对我不起作用。 如何修复这个错误: error: SWIFT_VERSION '5.0' is unsupported, suppo
从 Xcode 9.3 开始,我在我的模型中使用“Swift.ImplicitlyUnwrappedOptional.some”包裹了我的字符串变量 我不知道这是怎么发生的,但它毁了我的应用程序! 我
这个问题在这里已经有了答案: How to include .swift file from other .swift file in an immediate mode? (2 个答案) 关闭 6
我正在使用 Swift Package Manager 创建一个应用程序,我需要知道构建项目的配置,即 Debug 或 Release。我试图避免使用 .xcodeproj 文件。请有人让我知道这是否
有一个带有函数定义的文件bar.swift: func bar() { println("bar") } 以及一个以立即模式运行的脚本foo.swift: #!/usr/bin/xcrun s
我是一名优秀的程序员,十分优秀!