2014-08-31 4 views
4

시각 효과를 만들고 장치 카메라를 통해 캡처 한 비디오 프레임에 적용하는 대학의 과제가 있습니다. 현재 이미지 및 디스플레이를 가져올 수 있지만 픽셀 색상 값을 변경할 수는 없습니다.Swift에서 픽셀 단위로 이미지에 시각 효과 적용

샘플 버퍼를 imageRef 변수로 변환하고 UIImage로 변환하면 모든 것이 정상입니다.

그러나이 이미지를 가져 오려면이 색상 값을 픽셀 단위로 변경하고 싶습니다.이 예제에서는 음수 색상으로 변경합니다 (CIFilter를 사용할 수 없기 때문에 더 복잡한 작업을해야합니다).하지만 주석 처리 된 나쁜 액세스로 인해 추락했습니다. 당신은 아무 권한이없는 곳 newPixels UnsafeMutablePointer가 RawPointer 년에 지어졌으며 0000 포인트 메모리 및 내 의견으로는 할당되지 않은 메모리 공간을 가리키는으로 초기화하기 때문에

import UIKit 
import AVFoundation 

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { 

    let captureSession = AVCaptureSession() 
    var previewLayer : AVCaptureVideoPreviewLayer? 

    var captureDevice : AVCaptureDevice? 

    @IBOutlet weak var cameraView: UIImageView! 

    override func viewDidLoad() { 
    super.viewDidLoad() 

    captureSession.sessionPreset = AVCaptureSessionPresetMedium 

    let devices = AVCaptureDevice.devices() 

    for device in devices { 
     if device.hasMediaType(AVMediaTypeVideo) && device.position == AVCaptureDevicePosition.Back { 
     if let device = device as? AVCaptureDevice { 
      captureDevice = device 
      beginSession() 
      break 
     } 
     } 
    } 
    } 

    func focusTo(value : Float) { 
    if let device = captureDevice { 
     if(device.lockForConfiguration(nil)) { 
     device.setFocusModeLockedWithLensPosition(value) { 
      (time) in 
     } 
     device.unlockForConfiguration() 
     } 
    } 
    } 

    override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) { 
    var touchPercent = Float(touches.anyObject().locationInView(view).x/320) 
    focusTo(touchPercent) 
    } 

    override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) { 
    var touchPercent = Float(touches.anyObject().locationInView(view).x/320) 
    focusTo(touchPercent) 
    } 

    func beginSession() { 
    configureDevice() 

    var error : NSError? 
    captureSession.addInput(AVCaptureDeviceInput(device: captureDevice, error: &error)) 

    if error != nil { 
     println("error: \(error?.localizedDescription)") 
    } 

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 

    previewLayer?.frame = view.layer.frame 
    //view.layer.addSublayer(previewLayer) 

    let output = AVCaptureVideoDataOutput() 
    let cameraQueue = dispatch_queue_create("cameraQueue", DISPATCH_QUEUE_SERIAL) 
    output.setSampleBufferDelegate(self, queue: cameraQueue) 
    output.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_32BGRA] 

    captureSession.addOutput(output) 
    captureSession.startRunning() 
    } 

    func configureDevice() { 
    if let device = captureDevice { 
     device.lockForConfiguration(nil) 
     device.focusMode = .Locked 
     device.unlockForConfiguration() 
    } 
    } 

    // MARK : - AVCaptureVideoDataOutputSampleBufferDelegate 

    func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) { 
    let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 
    CVPixelBufferLockBaseAddress(imageBuffer, 0) 

    let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) 
    let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) 
    let width = CVPixelBufferGetWidth(imageBuffer) 
    let height = CVPixelBufferGetHeight(imageBuffer) 
    let colorSpace = CGColorSpaceCreateDeviceRGB() 

    var bitmapInfo = CGBitmapInfo.fromRaw(CGImageAlphaInfo.PremultipliedFirst.toRaw())! | CGBitmapInfo.ByteOrder32Little 

    let context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, bitmapInfo) 
    let imageRef = CGBitmapContextCreateImage(context) 

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0) 

    let data = CGDataProviderCopyData(CGImageGetDataProvider(imageRef)) as NSData 
    let pixels = data.bytes 

    var newPixels = UnsafeMutablePointer<UInt8>() 

    //for index in stride(from: 0, to: data.length, by: 4) { 

     /*newPixels[index] = 255 - pixels[index] 
     newPixels[index + 1] = 255 - pixels[index + 1] 
     newPixels[index + 2] = 255 - pixels[index + 2] 
     newPixels[index + 3] = 255 - pixels[index + 3]*/ 
    //} 

    bitmapInfo = CGImageGetBitmapInfo(imageRef) 
    let provider = CGDataProviderCreateWithData(nil, newPixels, UInt(data.length), nil) 

    let newImageRef = CGImageCreate(width, height, CGImageGetBitsPerComponent(imageRef), CGImageGetBitsPerPixel(imageRef), bytesPerRow, colorSpace, bitmapInfo, provider, nil, false, kCGRenderingIntentDefault) 

    let image = UIImage(CGImage: newImageRef, scale: 1, orientation: .Right) 
    dispatch_async(dispatch_get_main_queue()) { 
     self.cameraView.image = image 
    } 
    } 
} 

답변

1

당신은 픽셀 조작 루프에서 나쁜 액세스 할 수 있습니다 데이터를 저장합니다. 영업 이익이 게시 된 이후

첫째, 스위프트가 조금 변경

더 긴 설명하고 "솔루션"나는 약간의 변경을 위해

...,이 라인은 rawValue의 기능에 따라 수정되어야했다 :

//var bitmapInfo = CGBitmapInfo.fromRaw(CGImageAlphaInfo.PremultipliedFirst.toRaw())! | CGBitmapInfo.ByteOrder32Little 
    var bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedFirst.rawValue) | CGBitmapInfo.ByteOrder32Little 

또한 포인터에 몇 가지 변경 사항이 필요하므로 모든 변경 사항을 게시했습니다 (주석 표시가있는 원래 줄을 그대로 두었습니다).

let data = CGDataProviderCopyData(CGImageGetDataProvider(imageRef)) as NSData 

    //let pixels = data.bytes 
    let pixels = UnsafePointer<UInt8>(data.bytes) 

    let imageSize : Int = Int(width) * Int(height) * 4 

    //var newPixels = UnsafeMutablePointer<UInt8>() 

    var newPixelArray = [UInt8](count: imageSize, repeatedValue: 0) 

    for index in stride(from: 0, to: data.length, by: 4) { 
     newPixelArray[index] = 255 - pixels[index] 
     newPixelArray[index + 1] = 255 - pixels[index + 1] 
     newPixelArray[index + 2] = 255 - pixels[index + 2] 
     newPixelArray[index + 3] = pixels[index + 3] 
    } 

    bitmapInfo = CGImageGetBitmapInfo(imageRef) 
    //let provider = CGDataProviderCreateWithData(nil, newPixels, UInt(data.length), nil) 
    let provider = CGDataProviderCreateWithData(nil, &newPixelArray, UInt(data.length), nil) 

일부 설명 : 이전의 모든 픽셀 바이트 그것이 UnsafePointer로 픽셀을 변경하고 너무 대신 UINT8로 캐스트해야합니다. 그런 다음 새 픽셀에 대한 배열을 만들고 newPixels 포인터를 제거하고 배열에서 직접 작업했습니다. 마지막으로 공급자에게 새 배열에 대한 포인터를 추가하여 이미지를 만듭니다. 알파 바이트의 수정을 제거했습니다.

이 후 나는 매우 낮은 성능으로 10 초마다 1 이미지 (XCode를 통해 iPhone 5)에 넣은 내보기에 일부 부정적인 이미지를 얻을 수있었습니다. 그리고 이미지 뷰에 첫 번째 프레임을 표시하는 데 많은 시간이 걸립니다.

didOutputSampleBuffer 함수의 시작 부분에 captureSession.stopRunning()을 추가 할 때보다 빠른 응답이 있었고 처리가 완료된 후 captureSession.startRunning()으로 다시 시작되었습니다. 이것으로 나는 거의 1fps를 가졌다.

인터뷰에 응 해주셔서 감사합니다.

+0

이것이 분명한 경우 사과드립니다. 그러나이 경우 'captureOutput' 함수가 어떻게 트리거됩니까? – Ian

+2

원래 게시물 (첫 번째 행의 클래스 정의)에서 볼 수있는 뷰가 AVCaptureVideoDataOutputSampleBufferDelegate 여야합니다. 그런 다음 뷰는 서비스가 시작되고 버퍼에 데이터가있는 경우 함수를 트리거합니다. – KZD76

+0

아! 감사. 나는 그것을 놓쳤다. 왜 내가'videoDataOutput.setSampleBufferDelegate (self, queue : sessionQueue) '를 non-self가되도록 조정해야하는지 궁금 해서요 – Ian

관련 문제