【问题标题】:Android: How to improve the performance of my cameraAndroid:如何提高相机的性能
【发布时间】:2018-01-24 03:49:53
【问题描述】:

我开发了一个安卓相机应用!我使用camera2来实现摄像头模块!问题是当我用它拍照时,图像的大小太高了,大约9MB!!!所以我的画廊太慢了!这是什么原因?

我在不同的手机上测试过,图片大小不一样,但还是太高了!!我试过photo.compress(Bitmap.CompressFormat.JPEG, 50, out);这个代码来减小尺寸,但是图像质量对我来说太重要了,所以我不想降低分辨率!这是我的相机代码:

public class MainActivity extends AppCompatActivity {

    private Size previewsize;
    private Size jpegSizes[] = null;
    private TextureView textureView;
    private CameraDevice cameraDevice;
    private CaptureRequest.Builder previewBuilder;
    private CameraCaptureSession previewSession;
    private static VirtualFileSystem vfs;
    ImageButton getpicture;
    ImageButton btnShow;
    Button btnSetting;

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textureView = (TextureView) findViewById(R.id.textureview);
        textureView.setSurfaceTextureListener(surfaceTextureListener);
        getpicture = (ImageButton) findViewById(R.id.getpicture);
        btnShow = (ImageButton) findViewById(R.id.button2);
        btnSetting = (Button) findViewById(R.id.btn_setting);

        btnSetting.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, Setting.class));
            }
        });

        btnShow.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, Gallery2.class));
            }
        });

        getpicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getPicture();
            }
        });

    }

    void getPicture() {
        if (cameraDevice == null) {
            return;
        }
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
            if (characteristics != null) {
                jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
            }
            int width = 640, height = 480;
            if (jpegSizes != null && jpegSizes.length > 0) {
                width = jpegSizes[0].getWidth();
                height = jpegSizes[0].getHeight();
            }
            ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
            List<Surface> outputSurfaces = new ArrayList<Surface>(2);
            outputSurfaces.add(reader.getSurface());
            outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
            final CaptureRequest.Builder capturebuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            capturebuilder.addTarget(reader.getSurface());
            capturebuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            capturebuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
            ImageReader.OnImageAvailableListener imageAvailableListener = new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    Image image = null;
                    try {
                        image = reader.acquireLatestImage();
                        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
                        byte[] bytes = new byte[buffer.capacity()];
                        buffer.get(bytes);                     

                        Bitmap photo = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
                        photo.compress(Bitmap.CompressFormat.JPEG, 50, out);
                        save(out);
                        photo = Bitmap.createScaledBitmap(photo, 120, 120, false);
                        btnShow.setImageBitmap(photo);

                        save(out);
                    } catch (Exception ee) {
                    } finally {
                        if (image != null)
                            image.close();
                    }
                }

                void save(ByteArrayOutputStream bytes) {

                    File file12 = getOutputMediaFile();
                    OutputStream outputStream = null;
                    try {
                        outputStream = new FileOutputStream(file12);
                        outputStream.write(bytes.toByteArray());
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            if (outputStream != null)
                                outputStream.close();
                        } catch (Exception e) {
                        }
                    }
                }
            };
            HandlerThread handlerThread = new HandlerThread("takepicture");
            handlerThread.start();
            final Handler handler = new Handler(handlerThread.getLooper());
            reader.setOnImageAvailableListener(imageAvailableListener, handler);
            final CameraCaptureSession.CaptureCallback previewSSession = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
                    super.onCaptureStarted(session, request, timestamp, frameNumber);
                }

                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                    super.onCaptureCompleted(session, request, result);
                    startCamera();
                }
            };
            cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    try {
                        session.capture(capturebuilder.build(), previewSSession, handler);
                    } catch (Exception e) {
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {
                }
            }, handler);
        } catch (Exception e) {
        }
    }

    public void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String camerId = manager.getCameraIdList()[0];
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(camerId);
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            previewsize = map.getOutputSizes(SurfaceTexture.class)[0];
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

                return;
            }
            manager.openCamera(camerId, stateCallback, null);
        }catch (Exception e)
        {
        }
    }
    private TextureView.SurfaceTextureListener surfaceTextureListener=new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            openCamera();
        }
        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        }
        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }
        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        }
    };
    private CameraDevice.StateCallback stateCallback=new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            cameraDevice=camera;
            startCamera();
        }
        @Override
        public void onDisconnected(CameraDevice camera) {
        }
        @Override
        public void onError(CameraDevice camera, int error) {
        }
    };
    @Override
    protected void onPause() {
        super.onPause();
        if(cameraDevice!=null)
        {
            cameraDevice.close();
        }
    }
    void  startCamera()
    {
        if(cameraDevice==null||!textureView.isAvailable()|| previewsize==null)
        {
            return;
        }
        SurfaceTexture texture=textureView.getSurfaceTexture();
        if(texture==null)
        {
            return;
        }
        texture.setDefaultBufferSize(previewsize.getWidth(),previewsize.getHeight());
        Surface surface=new Surface(texture);
        try
        {
            previewBuilder=cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        }catch (Exception e)
        {
        }
        previewBuilder.addTarget(surface);
        try
        {
            cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    previewSession=session;
                    getChangedPreview();
                }
                @Override
                public void onConfigureFailed(CameraCaptureSession session) {
                }
            },null);
        }catch (Exception e)
        {
        }
    }
    void getChangedPreview()
    {
        if(cameraDevice==null)
        {
            return;
        }
        previewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
        HandlerThread thread=new HandlerThread("changed Preview");
        thread.start();
        Handler handler=new Handler(thread.getLooper());
        try
        {
            previewSession.setRepeatingRequest(previewBuilder.build(), null, handler);
        }catch (Exception e){}
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    private File getOutputMediaFile() {


        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
                .format(new Date());
        File mediaFile;
File(Environment.getExternalStorageDirectory()+"/MyCamAppCipher1"+"/" + mTime + ".jpg");

        mediaFile = new File("/myfiles.db"+"/" + mTime + ".jpg");
        return mediaFile;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

    }
}

【问题讨论】:

    标签: android image performance size android-camera2


    【解决方案1】:

    在这段代码中,您选择的是第一个可用的分辨率,通常是最高的。

            int width = 640, height = 480;
            if (jpegSizes != null && jpegSizes.length > 0) {
                width = jpegSizes[0].getWidth();
                height = jpegSizes[0].getHeight();
            }
    

    除了按照您所说的通过调用photo.compress(Bitmap.CompressFormat.JPEG, 50, out); 降低图像质量之外,您的替代方法是通过迭代jpegSizes 数组并选择较低的分辨率来选择较小的相机分辨率。

    考虑到这样做,您应该使用相对比较(即宽度 >= minWidth),或找到中间分辨率,但始终选择此数组中可用的分辨率之一。请注意,此数组因手机而异(即取决于相机特性)。

    例如,假设您至少需要 300 万像素 (2048x1536)。你可以有以下代码:

    int width = Integer.MAX_VALUE, height = Integer.MAX_VALUE;
    for (int i = 0; i < jpegSizes.length; i++) {
        int currWidth = jpegSizes[0].getWidth();
        int currHeight = jpegSizes[0].getHeight();
        if ((currWidth < width && currHeight < height) &&  // smallest resolution
            (currWidth > 2048  && currHeight > 1536)) {    // at least 3M pixels
            width = currWidth;
            height = currHeight;
        }
    }
    

    注意有两个条件:

    • 在第一行中,我们正在寻找可能的最小分辨率(总是小于前一个;还要注意widthheightInteger.MAX_VALUE 的初始值,这意味着至少第一个值将始终匹配此条件)
    • 在第二行中,我们确保 至少 3M 像素

    因此,所有组合此代码将选择可能的最小分辨率,至少 3M 像素。

    最后,您可以添加一个回退条件:如果没有找到匹配的分辨率(即widthheightInteger.MAX_VALUE),请按照您当前的操作选择第一个。

    【讨论】:

    • 我不明白如何选择较小的相机分辨率,请您解释一下吗?!?
    • 基本上,您必须迭代数组(一个常见的for 循环)并检查候选人是否符合您的期望。我会用一个例子来更新答案。
    【解决方案2】:

    我也用 camera2 制作了一个相机应用程序,几乎可以立即将图像保存到磁盘中,即使图片大小为 12 或 15 Mb(无需压缩到 50%)。当您说您的画廊很慢时...您是否将全尺寸图像加载到缩略图中?您是否显示全尺寸图像?尝试将小图片加载到您的缩略图中并显示。

    【讨论】:

    • 我在我的画廊中使用此代码作为缩略图:BitmapFactory.Options options.inSampleSize = 16;最终 int 拇指大小 = 64;尝试 { bmp = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeStream(new FileInputStream(file), null, options), THUMBSIZE, THUMBSIZE); } catch (FileNotFoundException e) { e.printStackTrace();好吗?!?!!??
    • 我看不出这段代码有什么问题。也许您可以尝试使用更大的 inSampleSize。您是在线程中执行此操作吗?
    • 是的,我在一个线程中做!但是我不保存缩略图,我应该保存它吗!?!
    • 这取决于...如果创建缩略图非常耗时,那么可以,您可以尝试保存缩略图并直接加载它们。
    • 如果我保存它,它会提高我画廊的性能吗?!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    • 2014-05-29
    • 2018-10-14
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多