2011-12-02 20 views
14

İlk olarak - bir süredir bunun üzerinde çalışıyor ve birkaç çözüm denedim, bu yüzden bana google'da bulduğunuz ilk bağlantıyı göndermeyin. Yapmam gereken şeyAndroid: kamera önizlemesini geri arama ile nasıl görüntüleyebilirim?

el kamera geri arama kullanarak kameradan önizleme görüntülemek istediğiniz, oldukça basit ve gerçek bir cihazda en az 15fps almak istiyorum. Renklere bile ihtiyacım yok, sadece gri tonlamalı görüntüyü önizlemem gerekiyor. Fotoğraf makinesinin görüntüleri YUV biçimindedir ve bir şekilde işlem yapmanız gerekir, bu da ana performans sorunudur. Ben, bu camera.setPreviewCallback daha hızlıdır) Ben camera.setPreviewCallbackWithBuffer (kullanıyorum Her durumda API 8.

kullanıyorum(). Önizlemeyi görüntülemiyorsam, yaklaşık 24 fps bulamıyorum. Yani sorun yok.

ben bu çözümleri denedim:

1. Ekran kamera önizleme bir Bitmap olarak bir SurfaceView üzerinde. Çalışıyor, ancak performans yaklaşık 6 fps.

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); 
bir doku olarak bir GLSurfaceView üzerinde


2. Ekran kamera önizleme. Burada sadece parlaklık verileri (greyscale image) görüntüleniyordum, ki bu oldukça kolaydır, her karede sadece bir arraycopy() gerektirir. Yaklaşık 12 fps alabiliyorum, ancak önizlemede bazı filtreler kullanmam gerekiyor ve öyle görünüyor ki, OpenGL ES 1'de hızlı bir şekilde yapılamıyor. Bu yüzden bu çözümü kullanamıyorum. Bunun bazı detayları another question. YUV verilerini işlemek için NDK kullanarak (GL) SurfaceView üzerinde


3. Ekran kamera önizleme. Bazı C işlevi ve NDK kullanan bir here çözümü buluyorum. Ama ben onu kullanmayı başaramadım, here some more details. Ama yine de, bu çözüm ByteBuffer'ı OpenGL'de bir doku olarak görüntülemeye döndürmek için yapılır ve önceki denemeden daha hızlı olmayacaktır. Bu yüzden, canvas.drawBitmap() ile çizilebilen int [] dizisini döndürmek için bunu değiştirmek zorundayım, ancak bunu yapmak için C'yi anlamıyorum.


Yani, denediğim denemelerde eksik veya bazı iyileştirmeler yapmanın başka bir yolu var mı?

Cevabınız için teşekkürler!

cevap

1

İstediğiniz bu değil mi? Sadece onResume() gibi init sonra yere, mizanpajınızda bir SurfaceView kullanın:

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

Böylesi daha hızlı geldikleri düz görünüme çerçeveleri gönderir. Eğer gri tonlama istiyorsanız

, setColorEffect("mono") ile kamera parametrelerini değiştirmek. çok temel ve basit efektler için

+0

Teşekkürler, ancak gerçekten geri çağırma ve görüntüleri manuel olarak kullanmam gerekiyor. Sadece gri tonlamalı görüntüye ihtiyacım yok, daha fazla filtre kullanıyorum, bu soruda bahsetmedim ... –

1

, bu etkiler FARKLI cihaza bağlı olarak yapmak anladım

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

yoktur. Örneğin, benim telefonumda (galaksi II), galaksinin 1 aksine, bir "mavi" renk tonu olduğu gibi komik bir efekt gibi görünüyor.

Profesyonel: Canlı önizleme olarak çalışıyor.

Bazı diğer kamera uygulamalarına baktım ve açıkçası bu problemle de karşılaştıkları açıktı. Peki ne yaptılar? Varsayılan kamera görüntüsünü yakalar, bitmap verisine filtre uygular ve bu resmi basit bir Resim Görünümünde gösterirler. Canlı önizlemede olduğu kadar havalı değil, ama performans sorunları ile karşılaşmayacaksınız.

+0

Teşekkürler, bu kamera efektlerini biliyorum, ancak kontrast ayarı, renk değişimi yapmak istedim Ama haklısınız, önizleme yapmak için gerçekten iyi bir çözüm bulunmuyor gibi görünüyor, bu yüzden açıkladığınız gibi bir şeyler yapmam gerekecek ...:/ –

7

Tam olarak aynı sorun üzerinde çalışıyorum, ancak sahip olduğunuz kadar uzağa sahip değilsiniz.

Pikselleri önce JPEG'ye kodlamadan doğrudan tuvale çizmeyi düşündünüz mü? OpenCV kiti içinde http://sourceforge.net/projects/opencvlibrary/files/opencv-android/2.3.1/OpenCV-2.3.1-android-bin.tar.bz2/download (aslında opencv kullanmıyor; endişelenmeyin), YUV piksellerini RGB'ye dönüştürmeyi ve sonra bunları doğrudan bir bitmap'e yazmayı gösteren öğretici-0-androidcamera adlı bir proje var. Eğer ihtiyaçlarınızı karşılayacak şekilde adapte etmesi gerekir Tabii

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(); 
} 

(. ex RGBA her kareyi ayırmıyor), ama bir başlangıç ​​olabilir:

ilgili kod

aslında. Sizin için işe yarayıp yaramadığını görmek isterim - şu anki problemlerinize karşı ortogonal sorunlarla mücadele ediyorum.

+1

Bunu yarın deneyeceğim ama ben YuvImage ve BitmapFactory sınıfları ile aynı veya daha az çalıştığı için performansta iyileşme olmayacağından emin olabilirsiniz. Ama ben bunu deneyeceğim ve göreceğiz. Teşekkürler. –

+1

Bu yüzden, beklediğim gibi, her durumda en kötü performansa sahipti, hatta bunu optimize etmeye çalıştım bile ... –

4

Sanırım Michael doğru yolda. İlk önce, RGB'den Gri Tonlamaya dönüştürmek için bu yöntemi deneyebilirsiniz. Açıkçası, onunkiyle neredeyse aynı şeyi yapıyor ama istediğin şey için biraz daha özlü.

//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; 
    } 
} 

}

İkincisi, çöp toplayıcı için bir ton iş oluşturmayın. Bitmap ve dizileriniz sabit bir boyutta olacak. OnFramePreview'de değil, bir kez oluşturun. Sonra

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(); 
    } 
}; 

Ve bu gibi görünüyor koymak istiyorum tuval:

şuna benzeyen bir şeyle sonunda edeceğiz yapmak

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); 
} 

Bu örnek bir Kamera önizlemesini gerçek zamanlı olarak kırpmış ve çarpmıştır, ancak siz fikir edinirsiniz. Gri tonlamada bir Nexus S'de yüksek FPS'de çalışır ve ihtiyaçlarınız için de çalışmalıdır.

+0

'İkincisi, çöp toplayıcı için bir ton çalışma yaratmayın. 'OnDraw'ınız bize başka türlü anlatıyor. –

0

Gri ​​tonlama verilerinin ilk x * y baytta olduğunu blog numaralı belgede okudum. Yuv, parlaklığı temsil etmelidir, bu yüzden veriler mükemmel bir gri tonlama olmasa da, oradadır. Her bir renk rgb cinsinden olduğu kadar parlak olmadığı için göreceli parlaklık için mükemmel, ancak gri tonlamalı değil. Yeşil genellikle parlaklık dönüşümlerinde daha güçlü bir ağırlık verilir. Bu yardımcı olur umarım!

0

GLES 1.0'ı kullanmak zorunda kalman için herhangi bir özel neden var mı? Android SDK: Get raw preview camera image without displaying it

Genellikle gles 2.0 ile birlikte Camera.setPreviewTexture() kullanarak bahseder:

değilse Çünkü, burada kabul cevaba bakınız. GLES 2.0'da, tüm ekran üzerinde dörtlü bir ekran oluşturabilir ve istediğiniz efekti yaratabilirsiniz.

Muhtemelen mümkün olan en hızlı yoldur.

+1

Örnek: https://github.com/google/grafika/blob/master/src/com/android/grafika/CameraCaptureActivity.java. Şu anda bir B & W görüntüsünü kırmızı renk kanalına yerleştiren bir gölgelendiriciye sahiptir ("rosyFragment" için arama). Aynı değeri R/G/B'ye atamak, bir B & W görüntüsü verecektir. – fadden