2016-06-05 57 views
10

Zajmuję się przetwarzaniem wideo w czasie rzeczywistym na iOS z szybkością 120 klatek na sekundę i chcę najpierw przetworzyć obraz na GPU (pomniejszone, przekonwertowane kolory itp., Które nie są wystarczająco szybko na CPU), a później na postprocessingu na CPU wykorzystującym OpenCV.Przetwarzanie danych z kamery na GPU (metal) i procesorze (OpenCV) na iPhonie

Jaki jest najszybszy sposób udostępnienia kanału z kamery między procesorem graficznym a procesorem za pomocą narzędzia Metal?

Innymi słowy rura będzie wyglądać następująco:

CMSampleBufferRef -> MTLTexture or MTLBuffer -> OpenCV Mat 

jestem konwersji CMSampleBufferRef -> MTLTexture następujący sposób

CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 

// textureRGBA 
{ 
    size_t width = CVPixelBufferGetWidth(pixelBuffer); 
    size_t height = CVPixelBufferGetHeight(pixelBuffer); 
    MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm; 

    CVMetalTextureRef texture = NULL; 
    CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, _textureCache, pixelBuffer, NULL, pixelFormat, width, height, 0, &texture); 
    if(status == kCVReturnSuccess) { 
     textureBGRA = CVMetalTextureGetTexture(texture); 
     CFRelease(texture); 
    } 
} 

Po moim metalu shader jest finised przekonwertować MTLTexture do OpenCV

cv::Mat image; 
... 
CGSize imageSize = CGSizeMake(drawable.texture.width, drawable.texture.height); 
int imageByteCount = int(imageSize.width * imageSize.height * 4); 
int mbytesPerRow = 4 * int(imageSize.width); 

MTLRegion region = MTLRegionMake2D(0, 0, int(imageSize.width), int(imageSize.height)); 
CGSize resSize = CGSizeMake(drawable.texture.width, drawable.texture.height); 
[drawable.texture getBytes:image.data bytesPerRow:mbytesPerRow fromRegion:region mipmapLevel:0]; 

Niektóre obserwacje:

1) Niestety MTLTexture.getBytes wydaje drogie (Kopiowanie danych z GPU do CPU) i trwa około 5ms na moim iPhone 5S, który jest zbyt dużo przy przetwarzaniu w ~ 100fps

2) Zauważyłem kilka osób? używać MTLBuffer zamiast MTLTexture z następującą metodą: metalDevice.newBufferWithLength(byteCount, options: .StorageModeShared) (patrz: Memory write performance - GPU CPU Shared Memory)

jednak CMSampleBufferRef i towarzyszące CVPixelBufferRef zarządzanego przez corevideo jest przypuszczenie.

+0

Procesor graficzny nie jest obsługiwany we wszystkich rozdzielczościach. Wiem, to nie jest twoja odpowiedź. Po prostu podaję informację o GPU. –

+0

wypróbowałeś GPUImage https://github.com/BradLarson/GPUImage –

+0

Próbowałem GPUImage ale największym bottlenect jest transfer danych z GPU do CPU. GPUImage używa OpenGL pod dobrym i przeciwnym do Metal API nie może mieć współużytkowanej pamięci. – pzo

Odpowiedz

4

Najszybszym sposobem wykonania tej czynności jest użycie tekstury MTLTexture wspieranej przez MTLBuffer; jest to specjalny rodzaj MTLTexture, który dzieli pamięć z MTLBufferem. Jednak twoje przetwarzanie C (openCV) będzie działało o jedną lub dwie ramki, jest to nieuniknione, ponieważ musisz przesłać polecenia do GPU (kodowanie), a GPU musi je wyrenderować, jeśli użyjesz waitUntilCompleted, aby upewnić się, że GPU jest zakończony, że po prostu przeżuwa procesor i jest marnotrawstwem.

Proces byłby następujący: najpierw utworzymy MTLBuffer, a następnie użyjemy metody MTLBuffer "newTextureWithDescriptor: offset: bytesPerRow:", aby stworzyć specjalną MTLTexture. Musisz wcześniej utworzyć specjalną MTLTexture (jako zmienną instancji), następnie musisz skonfigurować standardowy rendering (szybciej niż przy użyciu shaderów obliczeniowych), który zabierze tekst MTLTexture utworzony z CMSampleBufferRef i przekaże go do specjalnej MTLTexture, w które przechodzą możesz zmniejszyć i wykonać dowolną konwersję kolorów w jednym przebiegu. Następnie przesyłasz bufor poleceń do programu gpu, w kolejnym przejściu możesz po prostu wywołać [zawartość biblioteki MTL], aby pobrać wskaźnik do bajtów, które przywracają twoją specjalną MTLTexture do użycia w openCV.

Każda technika, która wymusza zatrzymanie w zachowaniu procesora/procesora graficznego, nigdy nie będzie wydajna, ponieważ połowa czasu zostanie poświęcona na czekanie, tj. Procesor czeka na zakończenie GPU, a procesor GPU musi poczekać na kolejne kodowania (gdy GPU działa, aby procesor zakodował następną klatkę i wykonał jakąkolwiek pracę openCV, zamiast czekać na zakończenie GPU).

Ponadto, gdy ludzie zwykle odwołują się do przetwarzania w czasie rzeczywistym, zazwyczaj odnoszą się do przetwarzania w czasie rzeczywistym (wizualne), wszystkie nowoczesne urządzenia iOS od 4s i powyżej mają częstotliwość odświeżania ekranu 60Hz, więc wszelkie opinie prezentowane szybciej, niż to nie ma sensu, ale jeśli potrzebujesz 2 klatek (przy 120 Hz), aby wykonać 1 (przy 60 Hz), musisz mieć niestandardowy zegar lub zmodyfikować CADisplayLink.

+0

Dobra wskazówka, że ​​renderowanie GPU (tekstury shaderów) może być ograniczone do 60fps - ma sens. Potrzebuję możliwie najmniejszego opóźnienia - mam niestandardowy naturalny interfejs użytkownika, który wykorzystuje dźwięk jako informację zwrotną dla użytkownika zamiast renderowania do wyświetlenia. Nie mam nic przeciwko procesorowi czekającemu na zakończenie GPU - chcę tylko przenieść niektóre preprocessing na GPU (dostosować kontrast, zmienić rozmiar filtra), są bardzo szybkie na GPU i dość wolno CPU (zdarzenie z NEON) biorąc pod uwagę mój napięty budżet obliczeniowy . Nie można przenieść (wydaje się niemożliwe?) Innych części do procesora graficznego, takich jak analiza konturów. Wydaje się, że GPU to dla mnie ślepy zaułek. – pzo

+0

Nie sądzę, że jest to ślepy zaułek, a przynajmniej byłoby stosunkowo łatwo skonfigurować potok pracujący z częstotliwością 60 Hz, w którym zakodowano i wykonano analizę konturową każdej klatki, a jednocześnie GPU wykonał niezbędne przetwarzanie wstępne, po uruchom i zoptymalizuj przy 60Hz (Metal Frame Debugger i Metal System Trace są bardzo przydatnymi narzędziami) spróbuj podłączyć go do 120Hz. Nigdy nie próbowałem używać timerów ani programu CADisplayLink tak szybko, że nie mogę ci tam pomóc, ale sprawdź: http://stackoverflow.com/questions/23885638/change-interval-of-cadisplaylink. – Gary

+0

Ponadto, nie jestem zaznajomiony z analizą konturów, ale przy użyciu funkcji obliczeniowych metalu można go przeprowadzić, ponieważ regulacja kontrastu lub zmiana rozmiaru nie będą miały wpływu na procesor graficzny (jeśli filtr jest złożony, należy zastosować LUT). Nawet ze standardowymi shadrami wierzchołków i fragmentów są często sztuczki, aby móc wykonywać niezbyt przyjazne układy GPU na GPU, zaimplementowałem algorytm etykietowania elementów połączonych za pomocą Metalu i nie był on zbyt daleko od wersji C dla małych obrazów – Gary