最近做项目遇到一个问题,iOS相机视频默认输出是yuv格式,然而图像处理类算法都是以rgb为输入,所以需要将yuv格式转为rgb格式,然后我用的是swift语言,在这里记录一下
yuv是一种图片储存格式,跟RGB格式类似。yuv中,y表示亮度,单独只有y数据就可以形成一张图片,只不过这张图片是灰色的。u和v表示色差(u和v也被称为:Cb-蓝色差,Cr-红色差), yuv比rgb数据占用字节数少,而且传输过程中可以y\u\v三种分别传输,所以最常见的是在直播推流类应用,yuv会比较常见。
多的不谈了,代码先跟上。
extension CVPixelBuffer {
public func toBGRA() throws -> CVPixelBuffer? {
let pixelBuffer = self
/// Check format
let pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer)
guard pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange || pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange else { return pixelBuffer }
// guard pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange else { return pixelBuffer }
/// Split plane
let yImage = pixelBuffer.with({ VImage(pixelBuffer: $0, plane: 0, pixelFormat: pixelFormat) })!
let cbcrImage = pixelBuffer.with({ VImage(pixelBuffer: $0, plane: 1, pixelFormat: pixelFormat) })!
/// Create output pixelBuffer
let outPixelBuffer = CVPixelBuffer.make(width: yImage.width, height: yImage.height, format: kCVPixelFormatType_32BGRA)!
/// Convert yuv to argb
var argbImage = outPixelBuffer.with({ VImage(pixelBuffer: $0, pixelFormat: pixelFormat) })!
try argbImage.draw(yBuffer: yImage.buffer, cbcrBuffer: cbcrImage.buffer)
/// Convert argb to bgra
argbImage.permute(channelMap: [3, 2, 1, 0])
// argbImage.permute(channelMap: [0, 1, 2, 3])
return outPixelBuffer
}
}
struct VImage {
let width: Int
let height: Int
let bytesPerRow: Int
var buffer: vImage_Buffer
let pixelFotmat: OSType
init?(pixelBuffer: CVPixelBuffer, plane: Int, pixelFormat: OSType) {
guard let rawBuffer = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, plane) else { return nil }
self.width = CVPixelBufferGetWidthOfPlane(pixelBuffer, plane)
self.height = CVPixelBufferGetHeightOfPlane(pixelBuffer, plane)
self.bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, plane)
self.buffer = vImage_Buffer(
data: UnsafeMutableRawPointer(mutating: rawBuffer),
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: bytesPerRow
)
self.pixelFotmat = pixelFormat
}
init?(pixelBuffer: CVPixelBuffer, pixelFormat: OSType) {
guard let rawBuffer = CVPixelBufferGetBaseAddress(pixelBuffer) else { return nil }
self.width = CVPixelBufferGetWidth(pixelBuffer)
self.height = CVPixelBufferGetHeight(pixelBuffer)
self.bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
self.buffer = vImage_Buffer(
data: UnsafeMutableRawPointer(mutating: rawBuffer),
height: vImagePixelCount(height),
width: vImagePixelCount(width),
rowBytes: bytesPerRow
)
self.pixelFotmat = pixelFormat
}
mutating func draw(yBuffer: vImage_Buffer, cbcrBuffer: vImage_Buffer) throws {
try buffer.draw(yBuffer: yBuffer, cbcrBuffer: cbcrBuffer, pixelFormat: self.pixelFotmat)
}
mutating func permute(channelMap: [UInt8]) {
buffer.permute(channelMap: channelMap)
}
}
extension CVPixelBuffer {
func with<T>(_ closure: ((_ pixelBuffer: CVPixelBuffer) -> T)) -> T {
CVPixelBufferLockBaseAddress(self, .readOnly)
let result = closure(self)
CVPixelBufferUnlockBaseAddress(self, .readOnly)
return result
}
static func make(width: Int, height: Int, format: OSType) -> CVPixelBuffer? {
var pixelBuffer: CVPixelBuffer? = nil
CVPixelBufferCreate(kCFAllocatorDefault,
width,
height,
format,
nil,
&pixelBuffer)
return pixelBuffer
}
}
extension vImage_Buffer {
mutating func draw(yBuffer: vImage_Buffer, cbcrBuffer: vImage_Buffer, pixelFormat: OSType) throws {
var yBuffer = yBuffer
var cbcrBuffer = cbcrBuffer
var conversionMatrix: vImage_YpCbCrToARGB
if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
conversionMatrix = {
var pixelRange = vImage_YpCbCrPixelRange(Yp_bias: 0, CbCr_bias: 128, YpRangeMax: 255, CbCrRangeMax: 255, YpMax: 255, YpMin: 1, CbCrMax: 255, CbCrMin: 0)
var matrix = vImage_YpCbCrToARGB()
vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange, &matrix, kvImage420Yp8_CbCr8, kvImageARGB8888, UInt32(kvImageNoFlags))
return matrix
}()
} else if (pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
conversionMatrix = {
var pixelRange = vImage_YpCbCrPixelRange(Yp_bias: 16, CbCr_bias: 128, YpRangeMax: 235, CbCrRangeMax: 240, YpMax: 235, YpMin: 16, CbCrMax: 240, CbCrMin: 16)
var matrix = vImage_YpCbCrToARGB()
vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange, &matrix, kvImage420Yp8_CbCr8, kvImageARGB8888, UInt32(kvImageNoFlags))
return matrix
}()
} else {
fatalError()
}
let error = vImageConvert_420Yp8_CbCr8ToARGB8888(&yBuffer, &cbcrBuffer, &self, &conversionMatrix, nil, 255, UInt32(kvImageNoFlags))
if error != kvImageNoError {
fatalError()
}
}
mutating func permute(channelMap: [UInt8]) {
vImagePermuteChannels_ARGB8888(&self, &self, channelMap, 0)
}
}
我这里是转的BGRA格式,用的是CVPixelBuffer,从CMSampleBuffer获取CVPixelBuffer也很简单,
let pixelBuffer: CVPixelBuffer? = CMSampleBufferGetImageBuffer(buffer)
guard let imagePixelBuffer = pixelBuffer else {
// 异常处理
}
然后直接调用就好了
let bgraImagePixelBuffer = try? pixelBuffer.toBGRA()
guard let oPixelBuffer = bgraImagePixelBuffer else {
// 异常处理
}
viencoding.com版权所有,允许转载,但转载请注明出处和原文链接: https://viencoding.com/article/265