2015-05-27 2 views
3

Мой текущий метод:CGImageRef более быстрый способ доступа к данным пикселов?

CGDataProviderRef provider = CGImageGetDataProvider(imageRef); 
imageData.rawData = CGDataProviderCopyData(provider); 
imageData.imageData = (UInt8 *) CFDataGetBytePtr(imageData.rawData); 

Я только получаю около 30 кадров в секунду. Я знаю, что часть хита производительности - это копирование данных, было бы неплохо, если бы я мог просто получить доступ к потоку байтов и не создавать его автоматически для меня.

Я пытаюсь заставить его обрабатывать CGImageRefs как можно быстрее, есть ли более быстрый способ?

Вот мои рабочие решения фрагмент кода:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    // Insert code here to initialize your application 
    //timer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 //2000.0 
    //           target:self 
    //          selector:@selector(timerLogic) 
    //          userInfo:nil 
    //          repeats:YES]; 
    leagueGameState = [LeagueGameState new]; 

    [self updateWindowList]; 
    lastTime = CACurrentMediaTime(); 






    // Create a capture session 
    mSession = [[AVCaptureSession alloc] init]; 

    // Set the session preset as you wish 
    mSession.sessionPreset = AVCaptureSessionPresetMedium; 

    // If you're on a multi-display system and you want to capture a secondary display, 
    // you can call CGGetActiveDisplayList() to get the list of all active displays. 
    // For this example, we just specify the main display. 
    // To capture both a main and secondary display at the same time, use two active 
    // capture sessions, one for each display. On Mac OS X, AVCaptureMovieFileOutput 
    // only supports writing to a single video track. 
    CGDirectDisplayID displayId = kCGDirectMainDisplay; 

    // Create a ScreenInput with the display and add it to the session 
    AVCaptureScreenInput *input = [[AVCaptureScreenInput alloc] initWithDisplayID:displayId]; 
    input.minFrameDuration = CMTimeMake(1, 60); 

    //if (!input) { 
    // [mSession release]; 
    // mSession = nil; 
    // return; 
    //} 
    if ([mSession canAddInput:input]) { 
     NSLog(@"Added screen capture input"); 
     [mSession addInput:input]; 
    } else { 
     NSLog(@"Couldn't add screen capture input"); 
    } 

    //**********************Add output here 
    //dispatch_queue_t _videoDataOutputQueue; 
    //_videoDataOutputQueue = dispatch_queue_create("com.apple.sample.capturepipeline.video", DISPATCH_QUEUE_SERIAL); 
    //dispatch_set_target_queue(_videoDataOutputQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)); 

    AVCaptureVideoDataOutput *videoOut = [[AVCaptureVideoDataOutput alloc] init]; 
    videoOut.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) }; 
    [videoOut setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; 

    // RosyWriter records videos and we prefer not to have any dropped frames in the video recording. 
    // By setting alwaysDiscardsLateVideoFrames to NO we ensure that minor fluctuations in system load or in our processing time for a given frame won't cause framedrops. 
    // We do however need to ensure that on average we can process frames in realtime. 
    // If we were doing preview only we would probably want to set alwaysDiscardsLateVideoFrames to YES. 
    videoOut.alwaysDiscardsLateVideoFrames = YES; 

    if ([mSession canAddOutput:videoOut]) { 
     NSLog(@"Added output video"); 
     [mSession addOutput:videoOut]; 
    } else {NSLog(@"Couldn't add output video");} 


    // Start running the session 
    [mSession startRunning]; 

    NSLog(@"Set up session"); 
} 




- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    //NSLog(@"Captures output from sample buffer"); 
    //CMFormatDescriptionRef formatDescription = CMSampleBufferGetFormatDescription(sampleBuffer); 
/* 
     if (self.outputVideoFormatDescription == nil) { 
      // Don't render the first sample buffer. 
      // This gives us one frame interval (33ms at 30fps) for setupVideoPipelineWithInputFormatDescription: to complete. 
      // Ideally this would be done asynchronously to ensure frames don't back up on slower devices. 
      [self setupVideoPipelineWithInputFormatDescription:formatDescription]; 
     } 
     else {*/ 
      [self renderVideoSampleBuffer:sampleBuffer]; 
     //} 
} 

- (void)renderVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer 
{ 
    //CVPixelBufferRef renderedPixelBuffer = NULL; 
    //CMTime timestamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); 

    //[self calculateFramerateAtTimestamp:timestamp]; 

    // We must not use the GPU while running in the background. 
    // setRenderingEnabled: takes the same lock so the caller can guarantee no GPU usage once the setter returns. 
    //@synchronized(_renderer) 
    //{ 
    // if (_renderingEnabled) { 
    CVPixelBufferRef sourcePixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 

    const int kBytesPerPixel = 4; 

    CVPixelBufferLockBaseAddress(sourcePixelBuffer, 0); 

    int bufferWidth = (int)CVPixelBufferGetWidth(sourcePixelBuffer); 
    int bufferHeight = (int)CVPixelBufferGetHeight(sourcePixelBuffer); 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(sourcePixelBuffer); 
    uint8_t *baseAddress = CVPixelBufferGetBaseAddress(sourcePixelBuffer); 

    int count = 0; 
    for (int row = 0; row < bufferHeight; row++) 
    { 
     uint8_t *pixel = baseAddress + row * bytesPerRow; 
     for (int column = 0; column < bufferWidth; column++) 
     { 
      count ++; 
      pixel[1] = 0; // De-green (second pixel in BGRA is green) 
      pixel += kBytesPerPixel; 
     } 
    } 

    CVPixelBufferUnlockBaseAddress(sourcePixelBuffer, 0); 


    //NSLog(@"Test Looped %d times", count); 

    CIImage *ciImage = [CIImage imageWithCVImageBuffer:sourcePixelBuffer]; 


    /* 
    CIContext *temporaryContext = [CIContext contextWithCGContext: 
              [[NSGraphicsContext currentContext] graphicsPort] 
                  options: nil]; 

    CGImageRef videoImage = [temporaryContext 
          createCGImage:ciImage 
          fromRect:CGRectMake(0, 0, 
               CVPixelBufferGetWidth(sourcePixelBuffer), 
               CVPixelBufferGetHeight(sourcePixelBuffer))]; 

    */ 

    //UIImage *uiImage = [UIImage imageWithCGImage:videoImage]; 

    // Create a bitmap rep from the image... 
    NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCIImage:ciImage]; 
    // Create an NSImage and add the bitmap rep to it... 
    NSImage *image = [[NSImage alloc] init]; 
    [image addRepresentation:bitmapRep]; 
    // Set the output view to the new NSImage. 
    [imageView setImage:image]; 

    //CGImageRelease(videoImage); 



    //renderedPixelBuffer = [_renderer copyRenderedPixelBuffer:sourcePixelBuffer]; 
    // } 
    // else { 
    //  return; 
    // } 
    //} 

    //Profile code? See how fast it's running? 
    if (CACurrentMediaTime() - lastTime > 3) //10 seconds 
    { 
     float time = CACurrentMediaTime() - lastTime; 
     [fpsText setStringValue:[NSString stringWithFormat:@"Elapsed Time: %f ms, %f fps", time * 1000/loopsTaken, (1000.0)/(time * 1000.0/loopsTaken)]]; 
     lastTime = CACurrentMediaTime(); 
     loopsTaken = 0; 
     [self updateWindowList]; 
     if (leagueGameState.leaguePID == -1) { 
      [statusText setStringValue:@"No League Instance Found"]; 
     } 
    } 
    else 
    { 
     loopsTaken++; 
    } 

} 

я получаю очень хороший 60 кадров в секунду, даже после того, как цикл по данным.

Он захватывает экран, я получаю данные, я изменяю данные, и я повторно показываю данные.

ответ

4

Какой «поток байтов» вы имеете в виду? CGImage представляет окончательные данные растрового изображения, но под капотом он все еще может быть сжат. Растровое изображение в настоящее время может храниться на графическом процессоре, поэтому для его получения может потребоваться выборка GPU-> CPU (что дорого, и ее следует избегать, когда она вам не нужна).

Если вы пытаетесь сделать это со скоростью более 30 кадров в секунду, вам может потребоваться переосмыслить, как вы атакуете проблему, и использовать инструменты, разработанные для этого, такие как Core Image, Core Video или Metal. Core Graphics оптимизирован для отображения, а не для обработки (и определенно не для обработки в реальном времени). Ключевое различие в инструментах, таких как Core Image, заключается в том, что вы можете выполнять большую часть своей работы на графическом процессоре без перетасовки данных обратно в CPU. Это абсолютно важно для поддержания быстрых трубопроводов. Когда это возможно, вы хотите избежать получения фактических байтов.

Если у вас уже есть CGImage, вы можете преобразовать его в CIImage с imageWithCGImage:, а затем использовать CIImage для дальнейшей обработки. Если вам действительно нужен доступ к байтам, ваши варианты - это тот, который вы используете, или для преобразования его в растровый контекст (который также потребует копирования) с CGContextDrawImage. Просто нет обещания, что CGImage имеет кучу битмап-байтов, которые будут входить в любой момент времени, на который вы можете смотреть, и он не предоставляет методы «блокировки вашего буфера», как вы можете найти в системах реального времени, таких как Core Video ,

Некоторые очень хорошие введения в обработку изображений с высокой скоростью из WWDC видео:

  • WWDC 2013 Сессии 509 Основных эффекты изображения и Techniques
  • WWDC 2014 Сессия 514 Авансов в основном изображении
  • WWDC 2014 Sessions 603-605 Работа с металлом
+0

Я извлекаю CGImageRef из CGWindowListCreateImage. Это экран, который моя программа читает. Я получаю данные пикселя из CGImageRef для прохождения цикла. Процесс без каких-либо фактических циклов по данным приводит меня к 30 кадрам в секунду. – Gan

+0

Обычный инструмент для этого - AV Foundation, который намного лучше оптимизирован для его обработки. https://developer.apple.com/library/mac/qa/qa1740/_index.html –

+0

Вы правы! Я получаю 60 кадров в секунду с AV Foundation даже после прокрутки пикселей и их изменения! – Gan

Смежные вопросы