2013-07-16 1 views
13

내 카메라 앱이 화면에 카메라 미리보기를 표시하고 백그라운드에서 처리합니다. 모든 장치, 제대로 카메라 미리보기 표시에Android 카메라 미리보기 데이터를 가져 오는 방법은 무엇입니까?

public final class CameraView extends SurfaceView implements 
      SurfaceHolder.Callback, Runnable, PreviewCallback { 

    public CameraView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     mHolder = getHolder(); 
     mHolder.addCallback(this); 
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

    void openCamera() { 
     // Called from parent activity after setting content view to CameraView 
     mCamera = Camera.open(); 
     mCamera.setPreviewCallbackWithBuffer(this); 
    } 

    public void surfaceCreated(SurfaceHolder holder) { 
     new Thread(this).start(); 

     // Set CameraView to the optimal camera preview size 

     final Camera.Parameters params = mCamera.getParameters(); 
     final List<Camera.Size> sizes = params.getSupportedPreviewSizes(); 
     final int screenWidth = ((View) getParent()).getWidth(); 
     int minDiff = Integer.MAX_VALUE; 
     Camera.Size bestSize = null; 

     if (getResources().getConfiguration().orientation 
       == Configuration.ORIENTATION_LANDSCAPE) { 
      // Find the camera preview width that best matches the 
      // width of the surface. 
      for (Camera.Size size : sizes) { 
       final int diff = Math.abs(size.width - screenWidth); 
       if (diff < minDiff) { 
        minDiff = diff; 
        bestSize = size; 
       } 
      } 
     } else { 
      // Find the camera preview HEIGHT that best matches the 
      // width of the surface, since the camera preview is rotated. 
      mCamera.setDisplayOrientation(90); 
      for (Camera.Size size : sizes) { 
       final int diff = Math.abs(size.height - screenWidth); 
       if (Math.abs(size.height - screenWidth) < minDiff) { 
        minDiff = diff; 
        bestSize = size; 
       } 
      } 
     } 

     final int previewWidth = bestSize.width; 
     final int previewHeight = bestSize.height; 

     ViewGroup.LayoutParams layoutParams = getLayoutParams(); 
     layoutParams.height = previewHeight; 
     layoutParams.width = previewWidth; 
     setLayoutParams(layoutParams); 

     params.setPreviewFormat(ImageFormat.NV21); 
     mCamera.setParameters(params); 

     int size = previewWidth * previewHeight * 
      ImageFormat.getBitsPerPixel(params.getPreviewFormat())/8; 
     mBuffer = new byte[size]; 
     mCamera.addCallbackBuffer(mBuffer); 

     mCamera.setPreviewDisplay(mHolder); 
     mCamera.startPreview(); 
    } 

    public void onPreviewFrame(byte[] data, Camera camera) { 
     CameraView.this.notify(); 
    } 

    public void run() { 
     mThreadRun = true; 
     while (mThreadRun) { 
      synchronized (this) { 
       this.wait(); 
       processFrame(mBuffer); // convert to RGB and rotate - not shown 
      } 
      // Request a new frame from the camera by putting 
      // the buffer back into the queue 
      mCamera.addCallbackBuffer(mBuffer); 
     } 

     mHolder.removeCallback(this); 
     mCamera.stopPreview(); 
     mCamera.setPreviewCallback(null); 
     mCamera.release(); 
     mCamera = null; 
    } 

    public void surfaceDestroyed(SurfaceHolder holder) { 
     mThreadRun = false; 
    } 
} 

, 그리고 (대부분의 에뮬레이터, 삼성 갤럭시 S3 등에 : 여기에 관련 코드, 응축은 (예를 들어, 오류 처리 또는 필드 선언이 표시되지 않습니다) 가능한 한 많이 .) mBuffer에 저장된 데이터도 정확합니다 (NV21에서 RGB 로의 변환 및 회전 이후). 그러나 수 많은 장치가 onPreviewFrame에 올바른 데이터를 제공하지 않습니다. 데이터가 수신 된 후 올바르게 RGB로 변환되고 있으므로 mBuffer에 제공된 원시 데이터에 문제가있는 것으로 보입니다. YV12 (별칭 YUV420p) 카메라 미리보기 형식과 관련된 this bug report을 보았지만 compatibility standard에 따라 지원해야하는 이전 기본 NV21 (별칭 YUV420sp)을 사용하고 있습니다 (7.5.3.2, 29 페이지 아래 참조).). 예를 들어

는이 장면을 위해 (삼성 갤럭시 탭 2에 카메라 미리보기 여기에 표시) : 탭 2에 mBuffer에 전달

enter image description here

데이터를 다음과 같습니다

enter image description here

및 Motorola Droid 4의 모양은 다음과 같습니다.

enter image description here

모든 기기에서 Android 카메라 미리보기 데이터를 가져 오는 올바른 방법은 무엇입니까?

편집 :processFrame()에 대해 OpenCV를 사용하여 RGB로 변환하고 회전합니다. this answerthis answer을 참조하십시오.

+0

어떻게 표시 할 이미지를 시도 할 수 있습니다? – EJTH

+0

@EJTH OpenCV ('COLOR_YUV420sp2RGBA' 플래그를 가진'cvtColor' 메소드)를 사용합니다. –

+0

그래서 당신은 안드로이드 장치가 아닌 무언가에 RGB 변환을 실행하고 있다고 생각하십니까? 호기심 때문에'YuvImage.compressToJpeg()'도 비슷한 왜곡 된 이미지를 생성합니까? – EJTH

답변

7

유일한 문제는 내가 미리보기 폭과 높이를 설정하지 않은 것이 었습니다 반환되는 실제 데이터의 크기보다 훨씬 큽니다 (기본값 인 미리보기 너비와 미리보기 높이에 비례). 일부 휴대 전화에서는 기본값이 previewWidth 및 previewHeight와 동일한 크기이므로 아무런 문제가 없었습니다.

2

미리보기 데이터에 크게 의존하는 바코드 스캐너를 사용하여 태양 아래 모든 버그를 봤습니다. 제 제안은 단순히 setPreviewFormat()으로 전화하지 않고 기본값을 사용하도록하는 것입니다. 다행스럽게도 기본값은 여기에 있습니다. setPreviewFormat()에 전화를 걸어 놓은 장치보다 기본 권한을 얻지 못하는 장치가 적은 것으로 보입니다. 그것을 적어도 시도해보십시오. 그렇지 않을 수도 있습니다. 나는 (previewWidth * previewHeight에 비례) 배열에 할당 된 높이와 너비가 될하는 경향이 있음을

params.setPreviewSize(previewWidth, previewHeight); 
mCamera.setParameters(params); 

이 의미 :

+0

나는 이미 그것을 시도했고, 같은 문제가있다. 코드의 관련 부분을 게시하는 데 관심이 있습니까? 두 가지 방법을 비교하여 답을 찾을 수도 있습니다. –

+0

http://code.google.com/p/zxing - CameraConfigurationManager를 찾으십시오. –

+0

감사합니다. 당신의 코드는'setPreviewCallbackWithBuffer' 대신에'setOneShotPreviewCallback'을 사용합니다, 그래서 나는 전환을 시도했고, 이미지는 똑같이 보입니다. 안드로이드가 이러한 장치의 카메라와 통합되는 방식에 거대한 버그가 있는지 궁금해집니다. –

3

또한 공정을 /이

public void takeSnapPhoto() { 
camera.setOneShotPreviewCallback(new Camera.PreviewCallback() { 
    @Override 
    public void onPreviewFrame(byte[] data, Camera camera) { 
     Camera.Parameters parameters = camera.getParameters(); 
     int format = parameters.getPreviewFormat(); 
     //YUV formats require more conversion 
     if (format == ImageFormat.NV21 || format == ImageFormat.YUY2 || format == ImageFormat.NV16) { 
      int w = parameters.getPreviewSize().width; 
      int h = parameters.getPreviewSize().height; 
      // Get the YuV image 
      YuvImage yuv_image = new YuvImage(data, format, w, h, null); 
      // Convert YuV to Jpeg 
      Rect rect = new Rect(0, 0, w, h); 
      ByteArrayOutputStream output_stream = new ByteArrayOutputStream(); 
      yuv_image.compressToJpeg(rect, 100, output_stream); 
      byte[] byt = output_stream.toByteArray(); 
      FileOutputStream outStream = null; 
      try { 
       // Write to SD Card 
       File file = createFileInSDCard(FOLDER_PATH, "Image_"+System.currentTimeMillis()+".jpg"); 
       //Uri uriSavedImage = Uri.fromFile(file); 
       outStream = new FileOutputStream(file); 
       outStream.write(byt); 
       outStream.close(); 
      } catch (FileNotFoundException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } finally { 
      } 
     } 
    } 
});} 
+0

이것은 사진을 찍어서 질문과 다른 기능인 SD 카드에 저장합니다. –

+1

내 시간을 절약하십시오! 감사! 나는 두 장의 사진을 아주 빨리 찍을 필요가 있었고,이 접근법은 나를 위해 가능했다. 그것은 나를 200 밀리 초 이상 걸리지 않습니다. 프레임 간 –

관련 문제