動画の1フレーム(CMSampleBuffer)の画素値を取得・操作する
元々はOpenCVのMatを使って画像の画素値を操作していたのですが、Swiftとの親和性だったりOpenCVそのものをiOSアプリに組み込むことのハードルの高さでつまずくことが多くなったので、OpenCVに頼らず画像の処理ができる(=画素値を弄れる)方法を検討してみました。3パターン試して、1だけうまくいっていますが一応3つとも掲載しています。
CMSampleBuffer型のsampleBuffer変数が渡されている(AVCaptureVideoDataOutputSampleBufferDelegateのcaptureOutputメソッド内で実行されている)ており、 このビューコントローラに存在するUIImageViewのimageに画像(UIImage)を渡すまでのコードです。
1.
CGImageを利用します。ポイントはCVPixelBufferGetBaseAddress(buffer)の戻り値をUnsafeMutablePointer
if let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
CVPixelBufferLockBaseAddress(buffer, 0)
let width = CVPixelBufferGetWidth(buffer)
let height = CVPixelBufferGetHeight(buffer)
let bytes = CVPixelBufferGetBytesPerRow(buffer)
let base = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(buffer))
for(var y = 0; y < height; y++) {
for(var x = 0; x < width; x++) {
let offset = 4*((width*y)+x);
base[offset] = base[offset] // R
base[offset+1] = base[offset] // G
base[offset+2] = base[offset] // B
base[offset+3] = base[offset+3] // A
}
}
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bi = CGBitmapInfo.ByteOrder32Little.rawValue | CGImageAlphaInfo.PremultipliedFirst.rawValue
let context = CGBitmapContextCreate(base, width, height, 8, bytes, colorSpace,bi)
if let cgImage = CGBitmapContextCreateImage(context) {
let image = UIImage(CGImage: cgImage)
CVPixelBufferUnlockBaseAddress(buffer, 0)
self.imageView.image = image
}
}
2.
UIGraphicsを利用します。こちらもUnsafeMutablePointerへのキャストが肝ですね。
この型を使うことで、なんと、実行時ではなくコンパイル時にSegmentation faultを検出できてしまうという。恐るべしSwift。
if let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
CVPixelBufferLockBaseAddress(buffer, 0)
let width = CVPixelBufferGetWidth(buffer)
let height = CVPixelBufferGetHeight(buffer)
let base = UnsafeMutablePointer<UInt8>(CVPixelBufferGetBaseAddress(buffer))
UIGraphicsBeginImageContext(CGSizeMake(CGFloat(width), CGFloat(height)));
let c = UIGraphicsGetCurrentContext()
let data = UnsafeMutablePointer<UInt8>(CGBitmapContextGetData(c))
for(var y = 0; y < height; y++) {
for(var x = 0; x < width; x++) {
let offset = 4*((width*y)+x);
data[offset] = base[offset] // R
data[offset+1] = base[offset] // G
data[offset+2] = base[offset] // B
data[offset+3] = base[offset+3] // A
}
}
self.imageView.image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
CVPixelBufferUnlockBaseAddress(buffer, 0)
}
3.
これはちょっと違いますが参考まで。CIImageを介した方法です。
画素単位での操作は出来ない?雰囲気ですが、目的のフィルタや検出器が既にあることが
わかっているなら圧倒的にシンプルで(恐らく)高速な解だと思われます。
if let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
let ciimage = CIImage(CVPixelBuffer: buffer)
if let filter = CIFilter(name: "CISepiaTone") {
filter.setValue(ciimage, forKey: kCIInputImageKey)
filter.setValue(0.8, forKey: kCIInputIntensityKey)
if let result:CIImage = filter.valueForKey(kCIOutputImageKey) as? CIImage {
let image = UIImage(CIImage: result)
self.imageView.image = image
}
}
}
参考URL
この記事は役に立ちましたか?
- EnglishWorm.com
- SinglesFan.com
- LmLab.net
- サイトマップ
- 運営者について