Похоже, ваш код успешно преобразует массив 512x512-float32 в массив 512x512-UInt8, поэтому я пишу этот ответ на основе некомментированной версии вашего кода.(Тем не менее, преобразование недостаточно эффективно и может улучшить ситуацию.)
ОБНОВЛЕНИЕ
Следующее описание не является правильным решением проблемы ОП.Просто хранится для записи.Пожалуйста, перейдите к ОБНОВЛЕННЫЙ КОД внизу этого ответа.
СТАРЫЙ КОД (НЕ правильное решение)
Прежде всего, худшим недостатком в коде являютсяследующие две строки:
imageRef = withUnsafePointer(to: &pixelValues, {
let data = UnsafeRawPointer(ptr.pointee).assumingMemoryBound(to: UInt8.self)
Первая строка выше передает указатель на [UInt8]?
, в Swift [UInt8]?
(он же Optional<Array<UInt8>>
) является 8-байтовой структурой, а не смежной областью, как C-arrays.
Второй более опасен.ptr.pointee
равен [UInt8]?
, но доступ к массивам Swift через указатель не гарантируется.А передача массива в UnsafeRawPointer.init(_:)
может создать временную область, которая будет освобождена сразу после вызова инициализатора.
Как вы знаете, доступ к висячему указателю иногда не причиняет вреда в некоторых ограниченных условиях, номожет привести к неожиданному результату в любое время.
Я бы написал что-то вроде этого:
func getSinglePlaneImage(inBuffer: MLMultiArray, width: Int, height: Int) -> UIImage {
//simulating pixels from MLMultiArray
//...
let pixelValues: [UInt8] = Array(repeating: 0, count: 1*512*512)
let bitsPerComponent = 8
let bytesPerPixel = 1
let bitsPerPixel = bytesPerPixel * 8
let bytesPerRow = bytesPerPixel * width
let totalBytes = height * bytesPerRow
let imageRef = pixelValues.withUnsafeBytes({bytes -> CGImage? in
var imageRef: CGImage?
let colorSpaceRef = CGColorSpaceCreateDeviceGray()
let bitmapInfo: CGBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
let data = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
let releaseData: CGDataProviderReleaseDataCallback = {_,_,_ in }
if let providerRef = CGDataProvider(dataInfo: nil, data: data, size: totalBytes, releaseData: releaseData) {
imageRef = CGImage(width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: bytesPerRow,
space: colorSpaceRef,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: false,
intent: .defaultIntent)
}
return imageRef
})
let newImage = UIImage(cgImage: imageRef!)
return newImage
}
Если вы хотите указатель, указывающий на начальный элемент массива, используйте withUnsafeBytes
и используйте указатель (на самом деле это UnsafeRawBufferPointer
) внутри аргумента замыкания.
Еще один, ваши pixels
или pixelValues
не должны быть необязательными.
Или же вы можете создать серое изображение с Float32
для каждого пикселя.
func getSinglePlaneImage(inBuffer: MLMultiArray, width: Int, height: Int) -> UIImage {
//simulating pixels from MLMultiArray
//...
let pixelValues: [Float32] = Array(repeating: 0, count: 1*512*512)
let bitsPerComponent = 32 //<-
let bytesPerPixel = 4 //<-
let bitsPerPixel = bytesPerPixel * 8
let bytesPerRow = bytesPerPixel * width
let totalBytes = height * bytesPerRow
let imageRef = pixelValues.withUnsafeBytes({bytes -> CGImage? in
var imageRef: CGImage?
let colorSpaceRef = CGColorSpaceCreateDeviceGray()
let bitmapInfo: CGBitmapInfo = [CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue),
.byteOrder32Little, .floatComponents] //<-
let data = bytes.baseAddress!.assumingMemoryBound(to: Float32.self)
let releaseData: CGDataProviderReleaseDataCallback = {_,_,_ in }
if let providerRef = CGDataProvider(dataInfo: nil, data: data, size: totalBytes, releaseData: releaseData) {
imageRef = CGImage(width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: bytesPerRow,
space: colorSpaceRef,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: false,
intent: CGColorRenderingIntent.defaultIntent)
}
return imageRef
})
let newImage = UIImage(cgImage: imageRef!)
return newImage
}
Оба работают как положено в моем проекте тестирования, но если вы обнаружите что-то не так, пожалуйста,сообщите мне.
ОБНОВЛЕННЫЙ КОД (Надеюсь, это правильное решение)
Я упустил тот факт, что CGDataProvider
сохраняет указатель при создании с init(dataInfo:data:size:releaseData:)
даже после CGImage
создано.Таким образом, на него можно ссылаться после завершения закрытия до withUnsafeBytes
, когда оно недопустимо.
В таких случаях лучше использовать CGDataProvider.init(data:)
.
func getSinglePlaneImage(inBuffer: MLMultiArray, width: Int, height: Int) -> UIImage {
var newImage: UIImage
//let floatPtr = inBuffer.dataPointer.bindMemory(to: Float32.self, capacity: inBuffer.count)
//let floatBuffer = UnsafeBufferPointer(start: floatPtr, count: inBuffer.count)
//let pixelValues: Data = Data((floatBuffer.lazy.map{
// UInt8(ImageProcessor.clamp((($0) + 1.0) * 128.0, minValue: 0.0, maxValue: 255.0))
//})
//simulating pixels from MLMultiArray
//...
let pixelValues = Data(count: 1*512*512) // <- ###
var imageRef: CGImage?
let bitsPerComponent = 8
let bytesPerPixel = 1
let bitsPerPixel = bytesPerPixel * bitsPerComponent
let bytesPerRow = bytesPerPixel * width
let colorSpaceRef = CGColorSpaceCreateDeviceGray()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
if let providerRef = CGDataProvider(data: pixelValues as CFData) { // <-###
imageRef = CGImage(width: width,
height: height,
bitsPerComponent: bitsPerComponent,
bitsPerPixel: bitsPerPixel,
bytesPerRow: bytesPerRow,
space: colorSpaceRef,
bitmapInfo: bitmapInfo,
provider: providerRef,
decode: nil,
shouldInterpolate: false,
intent: CGColorRenderingIntent.defaultIntent)
}
newImage = UIImage(cgImage: imageRef!)
return newImage
}
Насколькокак я пытался, это не дает сбой даже в реальном устройстве с количеством повторных касаний.Пожалуйста, попробуй.Спасибо за ваше терпение.