|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Accelerate |
|
import Foundation |
|
|
|
extension CVPixelBuffer { |
|
var size: CGSize { |
|
return CGSize(width: CVPixelBufferGetWidth(self), height: CVPixelBufferGetHeight(self)) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func resize(from source: CGRect, to size: CGSize) -> CVPixelBuffer? { |
|
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: self.size) |
|
guard rect.contains(source) else { |
|
os_log("Resizing Error: source area is out of index", type: .error) |
|
return nil |
|
} |
|
guard rect.size.width / rect.size.height - source.size.width / source.size.height < 1e-5 |
|
else { |
|
os_log( |
|
"Resizing Error: source image ratio and destination image ratio is different", |
|
type: .error) |
|
return nil |
|
} |
|
|
|
let inputImageRowBytes = CVPixelBufferGetBytesPerRow(self) |
|
let imageChannels = 4 |
|
|
|
CVPixelBufferLockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0)) |
|
defer { CVPixelBufferUnlockBaseAddress(self, CVPixelBufferLockFlags(rawValue: 0)) } |
|
|
|
|
|
guard |
|
let inputBaseAddress = CVPixelBufferGetBaseAddress(self)?.advanced( |
|
by: Int(source.minY) * inputImageRowBytes + Int(source.minX) * imageChannels) |
|
else { |
|
return nil |
|
} |
|
|
|
|
|
var croppedImage = vImage_Buffer( |
|
data: inputBaseAddress, height: UInt(source.height), width: UInt(source.width), |
|
rowBytes: inputImageRowBytes) |
|
|
|
let resultRowBytes = Int(size.width) * imageChannels |
|
guard let resultAddress = malloc(Int(size.height) * resultRowBytes) else { |
|
return nil |
|
} |
|
|
|
|
|
var resizedImage = vImage_Buffer( |
|
data: resultAddress, |
|
height: UInt(size.height), width: UInt(size.width), |
|
rowBytes: resultRowBytes |
|
) |
|
|
|
|
|
guard vImageScale_ARGB8888(&croppedImage, &resizedImage, nil, vImage_Flags(0)) == kvImageNoError |
|
else { |
|
return nil |
|
} |
|
|
|
let releaseCallBack: CVPixelBufferReleaseBytesCallback = { mutablePointer, pointer in |
|
if let pointer = pointer { |
|
free(UnsafeMutableRawPointer(mutating: pointer)) |
|
} |
|
} |
|
|
|
var result: CVPixelBuffer? |
|
|
|
|
|
let conversionStatus = CVPixelBufferCreateWithBytes( |
|
nil, |
|
Int(size.width), Int(size.height), |
|
CVPixelBufferGetPixelFormatType(self), |
|
resultAddress, |
|
resultRowBytes, |
|
releaseCallBack, |
|
nil, |
|
nil, |
|
&result |
|
) |
|
|
|
guard conversionStatus == kCVReturnSuccess else { |
|
free(resultAddress) |
|
return nil |
|
} |
|
|
|
return result |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func rgbData( |
|
isModelQuantized: Bool |
|
) -> Data? { |
|
CVPixelBufferLockBaseAddress(self, .readOnly) |
|
defer { CVPixelBufferUnlockBaseAddress(self, .readOnly) } |
|
guard let sourceData = CVPixelBufferGetBaseAddress(self) else { |
|
return nil |
|
} |
|
|
|
let width = CVPixelBufferGetWidth(self) |
|
let height = CVPixelBufferGetHeight(self) |
|
let sourceBytesPerRow = CVPixelBufferGetBytesPerRow(self) |
|
let destinationBytesPerRow = Constants.rgbPixelChannels * width |
|
|
|
|
|
var sourceBuffer = vImage_Buffer( |
|
data: sourceData, |
|
height: vImagePixelCount(height), |
|
width: vImagePixelCount(width), |
|
rowBytes: sourceBytesPerRow) |
|
|
|
|
|
guard let destinationData = malloc(height * destinationBytesPerRow) else { |
|
os_log("Error: out of memory", type: .error) |
|
return nil |
|
} |
|
defer { free(destinationData) } |
|
var destinationBuffer = vImage_Buffer( |
|
data: destinationData, |
|
height: vImagePixelCount(height), |
|
width: vImagePixelCount(width), |
|
rowBytes: destinationBytesPerRow) |
|
|
|
|
|
switch CVPixelBufferGetPixelFormatType(self) { |
|
case kCVPixelFormatType_32BGRA: |
|
vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags)) |
|
case kCVPixelFormatType_32ARGB: |
|
vImageConvert_BGRA8888toRGB888(&sourceBuffer, &destinationBuffer, UInt32(kvImageNoFlags)) |
|
default: |
|
os_log("The type of this image is not supported.", type: .error) |
|
return nil |
|
} |
|
|
|
|
|
let imageByteData = Data( |
|
bytes: destinationBuffer.data, count: destinationBuffer.rowBytes * height) |
|
|
|
if isModelQuantized { return imageByteData } |
|
|
|
let imageBytes = [UInt8](imageByteData) |
|
return Data(copyingBufferOf: imageBytes.map { Float($0) / Constants.maxRGBValue }) |
|
} |
|
} |
|
|