全网最简单实用Android摄像头开发,同时预览多个摄像头,双目摄像头,同时打开手机前后摄像头(红外摄像头、人脸识别、活体检测、Android Camera、缩放、焦距、旋转、镜像、截图保存)

如果你受够了网上那些乱七八糟的代码,你可以了解下我这个,能同时打开多个摄像头,在界面上预览,并且可以取得摄像头数据,byte[] 转为 Bitmap,保存为 jpg图片。

最近我们的某个项目要加上Android人脸识别,虽然有别人写好的“考勤”、“门口闸机”这些,但不能直接用于我们的项目,我们有自己的业务需求。

我们机器有3个摄像头,在进行人脸识别的时候,3个摄像头都要处于工作状态;分别是:一个主摄像头本来就一直处于拍照检测中的,另外的双目摄像头,一个用于人脸检测,另一个是红外摄像头于用进行活体检测。

当我开始调整的时候,才发现原来用的“androidx.camera”并不能同时打开多个摄像头,然后我去了解 camera2,发现使用比较复杂,我的想法是,简单实用就好,并不要非常强大的各种功能,后来我再去了解"camera1",即 “android.hardware.Camera”,这个虽然现在被弃用了,但是还能用,而且我感觉这个比较好理解,基于它我实现了需求。

如果你想要快速获得摄像头数据(甚至不用在界面显示预览),像下面这样即可:

    android.hardware.Camera mCamera;
    android.graphics.SurfaceTexture mSurface;

    /**
     * 摄像头快速预览并取得数据
     * 注意:mCamera和mSurface不要在本方法内定义
     */
    private void startCamera() {
        int cameraId = 0;
        try {
            // 打开相机
            mCamera = Camera.open(cameraId);
            // 设置图像尺寸
            Camera.Parameters params = mCamera.getParameters();
            params.setPreviewSize(640, 480);
            mCamera.setParameters(params);
            // 设置预览视图
            mSurface = new SurfaceTexture(0);
            mCamera.setPreviewTexture(mSurface);
            // 设置图像数据回调
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    // 取得图像数据,每一帧图像都回调到这里,在这里做图像处理
                    // 该方法高频调用,用RXJava做流控,data转换为Bitmap或Mat等
                }
            });
            // 开启预览
            mCamera.startPreview();
        } catch (Exception ignored) {
            // 发生异常
        }
    }

下面是我摄像头的相关代码,有需求的朋友可参考:

package cn.example.test.facesdk.camera;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.view.TextureView;

import java.io.IOException;
import java.util.List;

public class PreviewCamera {

    private Camera mCamera;
    private boolean mirror = false; // 镜像(左右翻转)
    private int rotate = -1; // 旋转(相机传感器方向相对于设备自然方向的角度)
    private int zoom = -1; // 焦距(有些摄像头可能不支持)

    public void setMirror(boolean mirror) {
        this.mirror = mirror;
    }

    public void setRotate(int rotate) {
        this.rotate = rotate;
    }

    public void setZoom(int zoom) {
        this.zoom = zoom;
    }

    public void startCamera(TextureView textureView, int cameraIndex, int width, int height, ICallback callback) {
        stopCamera();
        try {
            mCamera = Camera.open(cameraIndex);
            checkPreviewSize(width, height);
            // 设置参数(尺寸、对焦、焦距、旋转)
            Camera.Parameters params = mCamera.getParameters();
            //List<Integer> list = params.getSupportedPreviewFormats();
            params.setPreviewSize(width, height);
            List<String> focusModes = params.getSupportedFocusModes();
            if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            }
            if (this.zoom >= 0 && params.isZoomSupported() && this.zoom <= params.getMaxZoom()) {
                params.setZoom(this.zoom);
                mCamera.setParameters(params);
            }
            mCamera.setParameters(params);
            if (this.rotate >= 0) {
                mCamera.setDisplayOrientation(this.rotate);
            }
            // 预览初始化(左右翻转)
            initPreview(textureView);
            if (this.mirror) {
                textureView.setRotationY(180);
            }
            // 回调
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    if (callback != null) {
                        callback.onData(data, camera);
                    }
                }
            });
            if (callback != null) {
                callback.onSucc(mCamera);
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (callback != null) {
                callback.onError(e);
            }
        }
    }

    public void stopCamera() {
        try {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        } catch (Exception e) {
        }
    }

    private void checkPreviewSize(int width, int height) throws Exception {
        boolean sizeOk = false;
        List<Camera.Size> sizeList = mCamera.getParameters().getSupportedPreviewSizes();
        for (Camera.Size size : sizeList) {
            if (size.width == width && size.height == height) {
                sizeOk = true;
            }
        }
        if (!sizeOk) {
            throw new Exception(String.format("不支持该预览尺寸: [%d,%d]", width, height));
        }
    }

    private TextureView initPreview(TextureView textureView) {
        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
                try {
                    if (mCamera != null) {
                        mCamera.setPreviewTexture(surfaceTexture);
                        mCamera.startPreview();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
                stopCamera();
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

            }
        });
        return textureView;
    }

    public interface ICallback {
        void onSucc(Camera camera);

        void onData(byte[] data, Camera camera);

        void onError(Exception e);
    }
}

上面代码直接放到你项目即可,使用也非常简单明了,如下:

package cn.example.test.Activity;

import android.graphics.Bitmap;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.TextureView;

import cn.example.test.Activity.Base.BaseActivity;
import cn.example.test.R;
import cn.example.test.facesdk.camera.PreviewCamera;
import cn.example.test.facesdk.camera.PreviewCameraUtils;

public class MainActivity extends BaseActivity {

    PreviewCamera previewCamera1 = new PreviewCamera();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onResume() {
        super.onResume();
        start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        stop();
    }

    private void start() {
        TextureView textureView1 = findViewById(R.id.camera_preview);

        // 设置镜像(左右翻转)
        previewCamera1.setMirror(false);

        // 设置旋转角度
        //previewCamera1.setRotate(90);

        // 设置焦距
        //previewCamera1.setZoom(50);

        previewCamera1.startCamera(textureView1, 0, 640, 480, new PreviewCamera.ICallback() {
            @Override
            public void onData(byte[] data, Camera camera) {
                dealData1(data, camera);
            }

            @Override
            public void onSucc(Camera camera) {
            }

            @Override
            public void onError(Exception e) {
            }
        });
    }

    private void stop() {
        previewCamera1.stopCamera();
    }

    long lastTime1 = 0;

    private void dealData1(byte[] data, Camera camera) {
        // 控制下频率
        long currTime = System.currentTimeMillis();
        if (currTime - lastTime1 < 5000) {
            return;
        }
        lastTime1 = currTime;
        // 转换为Bitmpa
        Bitmap bitmap = PreviewCameraUtils.cameraDataToBitmap(data, camera);
        // 旋转&翻转
        bitmap = PreviewCameraUtils.bitmapXform(bitmap, 270, false, true);
        // 保存为jpg
        String path = PreviewCameraUtils.getSDCardPath() + "/" + System.currentTimeMillis() + ".jpg";
        PreviewCameraUtils.saveBitmapToFile(bitmap, path);
    }
}

上面就是核心的代码,已经可以把摄像头使用起来了,下面放几个常用的帮助方法:

package cn.example.test.facesdk.camera;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.os.Environment;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class PreviewCameraUtils {

    public static Bitmap cameraDataToBitmap(byte[] data, Camera camera) {
        Camera.Parameters parameters = camera.getParameters();
        int width = parameters.getPreviewSize().width;
        int height = parameters.getPreviewSize().height;
        int format = parameters.getPreviewFormat();
        Bitmap bitmap = yuvToBitmap(data, format, width, height);
        return bitmap;
    }

    /**
     * 摄像头数据转换为Bitmap
     *
     * @param data
     * @param format
     * @param width
     * @param height
     * @return
     */
    public static Bitmap yuvToBitmap(byte[] data, int format, int width, int height) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        YuvImage yuvImage = new YuvImage(data, format, width, height, null);
        yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, out);
        byte[] imageBytes = out.toByteArray();
        return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
    }

    /**
     * Bitmap进行旋转和翻转
     *
     * @param bitmap
     * @param rotate        旋转的角度
     * @param leftRightTurn 是否左右翻转
     * @param upDownTurn    是否上下翻转
     * @return
     */
    public static Bitmap bitmapXform(Bitmap bitmap, int rotate, boolean leftRightTurn, boolean upDownTurn) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Matrix matrix = new Matrix();
        if (leftRightTurn) {
            matrix.preScale(-1f, 1f);
        }
        if (upDownTurn) {
            matrix.preScale(1f, -1f);
        }
        if (rotate > 0) {
            matrix.postRotate(rotate);
        }
        Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
        return rotatedBitmap;
    }

    /**
     * 把Bitmap保存为jpg文件
     *
     * @param bitmap
     * @param filePath 绝对路径
     */
    public static void saveBitmapToFile(Bitmap bitmap, String filePath) {
        File file = new File(filePath);
        try {
            // 创建文件输出流
            FileOutputStream outputStream = new FileOutputStream(file);
            // 将bitmap写入输出流中
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
            // 刷新缓冲区
            outputStream.flush();
            // 关闭输出流
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 取得SDCard根路径
     *
     * @return
     */
    public static String getSDCardPath() {
        return "" + Environment.getExternalStorageDirectory();
    }

}

另外,记得声明权限,示例:

    <uses-feature
        android:name="android.hardware.camera.any"
        android:required="true" />
    <uses-permission android:name="android.permission.CAMERA" />
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED) {
            requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMIS_CODE);
        } else {
            
        }

-------------------(完)-------------------

猜你喜欢

转载自blog.csdn.net/envon123/article/details/132187957