2013-07-16 39 views
13

Ứng dụng máy ảnh của tôi hiển thị xem trước máy ảnh trên màn hình và cũng xử lý nó ở chế độ nền. Đây là mã có liên quan, đặc càng nhiều càng tốt (ví dụ như không xử lý hoặc lĩnh vực khai báo lỗi hiển thị):Cách tải dữ liệu xem trước của máy ảnh 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; 
    } 
} 

Trên tất cả các thiết bị, màn hình máy ảnh xem trước đúng cách, và trên hầu hết (giả lập, Samsung Galaxy S3, vv .) Dữ liệu được lưu trữ trong mBuffer cũng chính xác (sau khi chuyển đổi và xoay vòng NV21 sang RGB, tất nhiên). Tuy nhiên, một số thiết bị không cung cấp dữ liệu chính xác trong onPreviewFrame. Tôi chắc chắn rằng dữ liệu đang được chuyển đổi sang RGB một cách chính xác sau khi nó nhận được, do đó, vấn đề dường như trong dữ liệu thô được cung cấp cho mBuffer. Tôi đã nhận thấy this bug report liên quan đến định dạng xem trước máy ảnh YV12 (bí danh YUV420p), nhưng tôi đang sử dụng giá trị mặc định cũ, NV21 (bí danh YUV420sp), phải được hỗ trợ theo số compatibility standard (xem 7.5.3.2, cuối trang 29)).

Ví dụ, đối với cảnh này (hiển thị ở đây trong Máy ảnh Preview trên Samsung Galaxy Tab 2):

enter image description here

dữ liệu truyền cho mBuffer trên Tab 2 trông giống như:

enter image description here

và trên Motorola Droid 4 có dạng:

enter image description here

Cách chính xác để nhận dữ liệu xem trước máy ảnh Android trên tất cả các thiết bị là gì?

Chỉnh sửa: cho processFrame(), tôi đã sử dụng OpenCV để chuyển đổi thành RGB và xoay. Xem this answerthis answer.

+0

Làm thế nào để bạn hiển thị/quá trình hình ảnh? – EJTH

+0

@EJTH Tôi sử dụng OpenCV (phương thức 'cvtColor' với cờ' COLOR_YUV420sp2RGBA'). –

+0

Vì vậy, im đoán rằng bạn đang thực hiện chuyển đổi sang RGB trên một cái gì đó không phải là một thiết bị Android? Ngoài sự tò mò, 'YuvImage.compressToJpeg()' có tạo ra một hình ảnh méo mó tương tự không? – EJTH

Trả lời

7

Vấn đề duy nhất là tôi đã không thiết lập chiều rộng và chiều cao preview:

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

Điều này có nghĩa rằng chiều cao và chiều rộng tôi phân bổ cho các mảng (tỉ lệ với previewWidth * previewHeight) có xu hướng trở thành một lớn hơn rất nhiều so với kích thước của dữ liệu thực tế được trả về (tỷ lệ thuận với mặc định là chiều rộng xem trước và chiều cao xem trước)). Trên một số điện thoại, mặc định có cùng kích thước như previewWidth và previewHeight, do đó không có vấn đề gì.

2

Từ làm việc trên Máy quét mã vạch, phụ thuộc rất nhiều vào dữ liệu xem trước, tôi cảm thấy như tôi đã thấy mọi lỗi dưới ánh mặt trời. Đề nghị của tôi chỉ đơn giản là không gọi setPreviewFormat() và để cho nó sử dụng mặc định. Mặc định là những gì bạn muốn ở đây may mắn thay. Dường như có ít thiết bị hơn không nhận được quyền mặc định, so với thiết bị phát ra cuộc gọi đến setPreviewFormat(). Hãy thử rằng ít nhất, có thể hoặc có thể không được nó.

+0

Tôi đã thử điều đó và nó có cùng một vấn đề. Bạn có muốn đăng các phần có liên quan của mã không? Có thể tìm ra câu trả lời bằng cách so sánh hai phương pháp. –

+0

Đó là tất cả trên http://code.google.com/p/zxing - tìm CameraConfigurationManager. –

+0

Cảm ơn. Mã của bạn sử dụng 'setOneShotPreviewCallback' thay vì' setPreviewCallbackWithBuffer', vì vậy tôi đã thử chuyển đổi và hình ảnh trông giống hệt nhau. Tôi đang tự hỏi liệu có một lỗi khổng lồ trong cách Android tích hợp với các máy ảnh của các thiết bị này hay không. –

3

Bạn cũng có thể thử này

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

Thao tác này sẽ chụp ảnh và lưu vào thẻ SD, đây là chức năng khác so với câu hỏi. –

+1

Bạn tiết kiệm thời gian của tôi! Cảm ơn! Tôi cần chụp hai bức ảnh rất nhanh và cách tiếp cận này áp dụng cho tôi. Nó đưa tôi không quá 200 ms. giữa các khung –

Các vấn đề liên quan