2011-12-02 15 views
14

Po pierwsze - pracuję nad tym od jakiegoś czasu i wypróbowałem kilka rozwiązań, więc nie wysyłaj mi pierwszego linku znalezionego w Google.Android: jak wyświetlić podgląd kamery z oddzwanianiem?

Co muszę zrobić, to całkiem proste, chcę ręcznie wyświetlić podgląd z kamery za pomocą wywołania kamery i chcę uzyskać co najmniej 15 fps na prawdziwym urządzeniu. Nie potrzebuję nawet kolorów, wystarczy podgląd obrazu w skali szarości. Obrazy z kamery są w formacie YUV i trzeba to jakoś przetworzyć, co jest głównym problemem wydajności. Używam API 8.

We wszystkich przypadkach używam camera.setPreviewCallbackWithBuffer(), która jest szybsza niż camera.setPreviewCallback(). Wygląda na to, że nie mogę uzyskać tutaj około 24 fps, jeśli nie wyświetlam podglądu. Więc nie ma problemu.

Próbowałem te rozwiązania:

1. Ekran podglądu kamery na SurfaceView postaci bitmapy. Działa, ale wydajność wynosi około 6 klatek na sekundę.

baos = new ByteOutputStream(); 
yuvimage=new YuvImage(cameraFrame, ImageFormat.NV21, prevX, prevY, null); 

yuvimage.compressToJpeg(new Rect(0, 0, prevX, prevY), 80, baos); 
jdata = baos.toByteArray(); 

bmp = BitmapFactory.decodeByteArray(jdata, 0, jdata.length); // Convert to Bitmap, this is the main issue, it takes a lot of time 

canvas.drawBitmap(bmp , 0, 0, paint); 


2. Wyświetlacz podgląd kamery na GLSurfaceView jako tekstury. Tutaj wyświetlałem tylko dane luminancji (obraz w skali szarości), co jest dość łatwe, wymaga tylko jednej arraycopy() na każdej ramce. Mogę uzyskać około 12 fps, ale muszę zastosować niektóre filtry do podglądu i wydaje się, że nie można tego zrobić szybko w OpenGL ES 1. Nie mogę użyć tego rozwiązania. Niektóre szczegóły tego w another question.


3. Wyświetlanie podglądu kamery na (GL) SurfaceView wykorzystaniem NDK do przetwarzania danych YUV. Znajduję rozwiązanie here, które używa jakiejś funkcji C i NDK. Ale nie udało mi się go użyć, here some more details. W każdym razie, to rozwiązanie jest zrobione, aby zwrócić ByteBuffer, aby wyświetlić go jako teksturę w OpenGL i nie będzie szybszy niż poprzednia próba. Musiałbym więc zmodyfikować go, aby zwrócić tablicę int [], którą można narysować za pomocą canvas.drawBitmap(), ale nie rozumiem wystarczająco C, aby to zrobić.


Czy jest jakiś inny sposób, którego mi brakuje lub jakaś poprawa prób, które próbowałem?

Dzięki za odpowiedź!

Odpowiedz

1

Czy tego nie chcesz? Wystarczy użyć SurfaceView w układzie, a potem gdzieś w pliku init jak onResume():

SurfaceView surfaceView = ... 
SurfaceHolder holder = surfaceView.getHolder(); 
... 
Camera camera = ...; 
camera.setPreviewDisplay(holder); 

To po prostu wysyła klatki prosto do widoku tak szybko, jak przylatują.

Jeśli chcesz uzyskać skalę szarości, zmień parametry kamery za pomocą setColorEffect("mono").

+0

Dzięki, ale naprawdę muszę używać wywołań zwrotnych i wyświetlać obrazy ręcznie. Nie potrzebuję tylko obrazu w skali szarości, używam więcej filtrów, o których nie wspomniałem w swoim pytaniu ... –

1

Na efekty bardzo podstawowe i proste, nie ma

Camera.Parameters parameters = mCamera.getParameters(); 
parameters.setColorEffect(Parameters.EFFECT_AQUA); 

zorientowali się, że ten wpływ nie różnie w zależności od urządzenia. Na przykład na moim telefonie (galaxy s II) wygląda to trochę jak efekt komiksu, ponieważ w przeciwieństwie do galaktyki 1 jest "tylko" niebieskim odcieniem.

Jest pro: działa jako podgląd na żywo.

Rozejrzałem się po kilku innych aplikacjach z aparatem fotograficznym, które oczywiście również napotkały ten problem. Więc co oni zrobili? Przechwytują domyślny obraz z kamery, stosując filtr do danych bitmapowych i wyświetlają ten obraz w prostym widoku obrazu. Z pewnością nie jest to takie fajne, jak w przypadku podglądu na żywo, ale nigdy nie napotkasz problemów z wydajnością.

+0

Dzięki, wiem o tych efektach aparatu, ale chciałem zrobić korektę kontrastu, zmianę koloru itp. Ale masz rację, wygląda na to, że naprawdę nie jest dobrym rozwiązaniem do podglądu na żywo, więc będę musiał zrobić coś takiego, jak opisałeś ...:/ –

7

Pracuję nad dokładnie ten sam problem, ale nie mam tak daleko, jak masz.

Czy zastanawiałeś się nad rysowaniem pikseli bezpośrednio na płótnie bez uprzedniego zakodowania ich w formacie JPEG? Wewnątrz zestawu OpenCV http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/OpenCV-2.3.1-android-bin.tar.bz2/download (który w rzeczywistości nie korzysta z opencv, nie martw się), istnieje projekt o nazwie tutorial-0-androidcamera, który demonstruje konwersję pikseli YUV do RGB, a następnie zapisuje je bezpośrednio w bitmapie.

Odpowiedni kod jest zasadniczo:

public void onPreviewFrame(byte[] data, Camera camera, int width, int height) { 
    int frameSize = width*height; 
    int[] rgba = new int[frameSize+1]; 

    // Convert YUV to RGB 
    for (int i = 0; i < height; i++) 
     for (int j = 0; j < width; j++) { 
      int y = (0xff & ((int) data[i * width + j])); 
      int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0])); 
      int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1])); 
      y = y < 16 ? 16 : y; 

      int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128)); 
      int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128)); 
      int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128)); 

      r = r < 0 ? 0 : (r > 255 ? 255 : r); 
      g = g < 0 ? 0 : (g > 255 ? 255 : g); 
      b = b < 0 ? 0 : (b > 255 ? 255 : b); 

      rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r; 
     } 

    Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 
    bmp.setPixels(rgba, 0/* offset */, width /* stride */, 0, 0, width, height); 
    Canvas canvas = mHolder.lockCanvas(); 
    if (canvas != null) { 
     canvas.drawBitmap(bmp, (canvas.getWidth() - width)/2, (canvas.getHeight() - height)/2, null); 
     mHolder.unlockCanvasAndPost(canvas); 
    } else { 
     Log.w(TAG, "Canvas is null!"); 
    } 
    bmp.recycle(); 
} 

Oczywiście trzeba by dostosować go do swoich potrzeb (. Ex RGBA nie przydzielając każdej ramki), ale może to być początek. Chciałbym zobaczyć, czy to działa dla ciebie, czy nie - wciąż mam problemy ortogonalne do twoich.

+1

Spróbuję tego jutro, ale jestem całkiem pewne, że nie będzie poprawy wydajności, ponieważ działa mniej więcej tak samo, jak wersja z klasami YuvImage i BitmapFactory. Ale spróbuję, a zobaczymy. Dzięki. –

+1

Tak więc, jak się spodziewałem, ma najgorszą wydajność ze wszystkich przypadków, nawet gdy próbowałem ją nieco zoptymalizować ... –

4

Myślę, że Michael jest na dobrej drodze. Najpierw możesz wypróbować tę metodę, aby przekonwertować z RGB do skali szarości. Najwyraźniej robi to prawie to samo, co jego, ale trochę bardziej zwięźle, o czym marzysz.

//YUV Space to Greyscale 
static public void YUVtoGrayScale(int[] rgb, byte[] yuv420sp, int width, int height){ 
    final int frameSize = width * height; 
    for (int pix = 0; pix < frameSize; pix++){ 
     int pixVal = (0xff & ((int) yuv420sp[pix])) - 16; 
     if (pixVal < 0) pixVal = 0; 
     if (pixVal > 255) pixVal = 255; 
     rgb[pix] = 0xff000000 | (pixVal << 16) | (pixVal << 8) | pixVal; 
    } 
} 

}

Po drugie, nie stwarzają mnóstwo pracy dla śmieciarza. Twoje bitmapy i tablice będą miały stały rozmiar. Utwórz je raz, a nie w onFramePreview.

Doing że będziesz skończyć z czymś, co wygląda tak:

public PreviewCallback callback = new PreviewCallback() { 
    @Override 
    public void onPreviewFrame(byte[] data, Camera camera) { 
     if ((mSelectView == null) || !inPreview) 
      return; 
     if (mSelectView.mBitmap == null) 
     { 
      //initialize SelectView bitmaps, arrays, etc 
      //mSelectView.mBitmap = Bitmap.createBitmap(mSelectView.mImageWidth, mSelectView.mImageHeight, Bitmap.Config.RGB_565); 
      //etc 

     } 
     //Pass Image Data to SelectView 
     System.arraycopy(data, 0, mSelectView.mYUVData, 0, data.length); 
     mSelectView.invalidate(); 
    } 
}; 

a następnie płótnie gdzie chcesz umieścić wygląda to tak:

class SelectView extends View { 
Bitmap mBitmap; 
Bitmap croppedView; 
byte[] mYUVData; 
int[] mRGBData; 
int mImageHeight; 
int mImageWidth; 

public SelectView(Context context){ 
    super(context); 
    mBitmap = null; 
    croppedView = null; 
} 

@Override 
protected void onDraw(Canvas canvas){ 
    if (mBitmap != null) 
    { 
     int canvasWidth = canvas.getWidth(); 
     int canvasHeight = canvas.getHeight(); 
     // Convert from YUV to Greyscale 
     YUVtoGrayScale(mRGBData, mYUVData, mImageWidth, mImageHeight); 
      mBitmap.setPixels(mRGBData, 0, mImageWidth, 0, 0, mImageWidth, mImageHeight); 
      Rect crop = new Rect(180, 220, 290, 400); 
     Rect dst = new Rect(0, 0, canvasWidth, (int)(canvasHeight/2)); 
     canvas.drawBitmap(mBitmap, crop, dst, null); 
    } 
    super.onDraw(canvas); 
} 

Ten przykład pokazuje przycięty i zniekształcony wybór podglądu kamery w czasie rzeczywistym, ale masz pomysł. Działa na wysokim FPS na Nexusie S w skali szarości i powinien również działać na Twoje potrzeby.

+0

'Po drugie, nie twórz pracy dla śmieciarza". Twój 'onDraw' mówi nam inaczej. –

0

Wydaje mi się, że przeczytałem w numerze blog, że dane w skali szarości znajdują się w pierwszych x * y bajtów. Yuv powinien reprezentować luminację, więc dane tam są, chociaż nie jest to idealnie skala szarości. Świetnie nadaje się do względnej jasności, ale nie do skali szarości, ponieważ każdy kolor nie jest tak jasny jak każdy inny w rgb. Kolor zielony ma zwykle większą wagę w konwersji światła. Mam nadzieję że to pomoże!

0

Czy istnieje jakiś szczególny powód, dla którego zmuszeni jesteście używać GLES 1.0?

Bo jeśli nie, zobacz zaakceptowane odpowiedź tutaj: Android SDK: Get raw preview camera image without displaying it

Ogólnie wspomina używając Camera.setPreviewTexture() w połączeniu z GLES 2.0. W GLES 2.0 można renderować pełnoekranowe quady na całym ekranie i tworzyć dowolne efekty.

To najprawdopodobniej najszybszy możliwy sposób.

+1

Przykład: https://github.com/google/grafika/blob/master/src/com/android/grafika/CameraCaptureActivity.java. Obecnie posiada moduł cieniujący, który umieszcza obraz czarno-biały w kanale koloru czerwonego (szukaj "rosyFragment"). Przypisanie tej samej wartości R/G/B dałoby obraz czarno-biały. – fadden

Powiązane problemy