android 相机预览拉伸问题

相机拉升 是因为 SurfaceView预览 与 相机分辨率不一致导致的

        <com.android.opencvapplication.camera.CameraView
            android:id="@+id/cameraView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
public class MatcherActivity{
    .....
    @Override
    protected void onStart() {
        cameraView.setListener(this);
        cameraView.startPreview(Camera.CameraInfo.CAMERA_FACING_BACK, CameraManager.PREVIEW_SIZE_480P);
    }

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

CameraView 讲解

设置 SurfaceView 的大小和 camera 的大小一致;
onMeasure 是重点,根据camera 大 设置 surfaceView大小

public class CameraView extends SurfaceView implements SurfaceHolder.Callback, CameraManager.OnCameraManagerListener {
    .....
    private CameraManager mCameraManager;
    ....
    public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        getHolder().addCallback(this);
        mCameraManager = new CameraManager();
        mCameraManager.setListener(this);
    }

    onMeasure 是重点,根据camera 大 设置 surfaceView大小

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = resolveSize(0, widthMeasureSpec);
        int height = resolveSize(width * mCameraSize.width / mCameraSize.height, heightMeasureSpec);
        setMeasuredDimension(width, height);
        L.d("onMeasure");
    }

    @Override
    public void onCamera(Camera camera) {
        L.d("onCamera");
        mCameraSize = camera.getParameters().getPreviewSize();
    }

   public void startPreview(int cameraId, int dpi) {
        mCameraManager.openCamera(cameraId, dpi);
        if (mSurfaceHolder != null) {
            mCameraManager.startPreview(mSurfaceHolder);
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mCameraManager.releaseCamera();
    }
}

CameraManager 代码讲解

public class CameraManager implements Camera.PreviewCallback {
    ....
    private int previewSize = PREVIEW_SIZE_720P;
    public static final int PREVIEW_SIZE_1080P = 1080;
    public static final int PREVIEW_SIZE_960P = 960;
    public static final int PREVIEW_SIZE_720P = 720;
    public static final int PREVIEW_SIZE_480P = 480;

    public void openCamera(int cameraId) {
        openCamera(cameraId, PREVIEW_SIZE_720P);
    }

    /**
     * @param cameraId    前后摄像头Id
     * @param previewSize def PREVIEW_SIZE_720P
     */
    public void openCamera(int cameraId, int previewSize) {
       ....
       获取相机硬件预览大小
        mParameters = mCamera.getParameters();
        mPictureSizes = mParameters.getSupportedPictureSizes();
        mPreviewSizes = mParameters.getSupportedPreviewSizes();
        Collections.reverse(mPictureSizes);
        Collections.reverse(mPreviewSizes);

        //选择合适的分辨率
        Camera.Size size = calculateCameraFrameSize(mPreviewSizes, previewSize, 0);
        Log.i(TAG, "Camera.Size:" + size.width + "x" + size.height);
        //设置预览 与 照片
        setParameters(previewSize, size.width, size.height);
        addCameraCallBack();
    }

    private void addCameraCallBack() {
        ....
        返回相机
        if (mListener != null)
            mListener.onCamera(mCamera);
    }

    private void setParameters(int pictureSize, int width, int height) {
        float rate = (float) width / height;//比例
        rate = Float.valueOf(String.format(Locale.getDefault(), "%.2f", rate));
        Log.i(TAG, "相机预览:" + width + "x" + height + "--" + pictureSize + " dpi----rate:" + rate + "---");
        Camera.Size size;

        .....
        设置预览大小
        size = calculateCameraFrameSize(mPreviewSizes, pictureSize, rate);
        mParameters.setPreviewSize(size.width, size.height);
        Log.i(TAG, "设置预览:w = " + size.width + "x" + size.height + "--rate:" + (float) size.width / size.height);
        mParameters.setPreviewFormat(ImageFormat.NV21);
        .....
    }

     protected Camera.Size calculateCameraFrameSize(List<Camera.Size> supportedSizes, int previewSize, float rate) {
        int k = 0;
        int calcWidth = 0;
        int calcHeight = 0;
        for (int i = 0; i < supportedSizes.size(); i++) {
            Camera.Size size = supportedSizes.get(i);
            int width = size.width;
            int height = size.height;
            if (equalRate(size, rate)) {
                Log.i(TAG, "CameraSize:" + width + "x" + height + "--rate:" + (float) width / height);
                //1088 这个像素容易出错
                if (width >= calcWidth && height >= calcHeight && height <= previewSize && height != 1088) {//960
                    calcWidth = width;
                    calcHeight = height;
                    k = i;
                }
            }
        }
        return supportedSizes.get(k);
    }
}

完整的 CameraManager 代码

import android.graphics.ImageFormat;
import android.hardware.Camera;
import android.os.Build;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceHolder;

import com.android.opencvapplication.util.L;

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

/**
 * Created by My on 2017/12/13.
 * 相机管理
 */

public class CameraManager implements Camera.PreviewCallback {
    private String TAG = CameraManager.class.getSimpleName();

    private int previewSize = PREVIEW_SIZE_720P;
    public static final int PREVIEW_SIZE_1080P = 1080;
    public static final int PREVIEW_SIZE_960P = 960;
    public static final int PREVIEW_SIZE_720P = 720;
    public static final int PREVIEW_SIZE_480P = 480;

    private int numCameras = Camera.getNumberOfCameras(); // 初始化摄像头数量
    private Camera mCamera;
    private Camera.Parameters mParameters;
    private List<Camera.Size> mPictureSizes;
    private List<Camera.Size> mPreviewSizes;
    private Camera.Parameters parameters;
    private byte[] mBuffer;
    private boolean previewing;
    private OnCameraManagerListener mListener;

    public void setListener(OnCameraManagerListener listener) {
        mListener = listener;
    }

    public boolean isPreviewing() {
        return previewing;
    }

    public void openCamera(int cameraId) {
        openCamera(cameraId, PREVIEW_SIZE_720P);
    }

    /**
     * @param cameraId    前后摄像头Id
     * @param previewSize def PREVIEW_SIZE_720P
     */
    public void openCamera(int cameraId, int previewSize) {
        this.previewSize = previewSize;
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        //有效的 cameraId 时,打开当前摄像头,否则打开后置摄像头
        cameraId = cameraId >= 0 && cameraId < numCameras ? cameraId : Camera.CameraInfo.CAMERA_FACING_BACK;
        mCamera = Camera.open(cameraId);

        mCamera.setDisplayOrientation(90);//设置旋转90度

        mParameters = mCamera.getParameters();
        mPictureSizes = mParameters.getSupportedPictureSizes();
        mPreviewSizes = mParameters.getSupportedPreviewSizes();
        Collections.reverse(mPictureSizes);
        Collections.reverse(mPreviewSizes);

        //选择合适的分辨率
        Camera.Size size = calculateCameraFrameSize(mPreviewSizes, previewSize, 0);
        Log.i(TAG, "Camera.Size:" + size.width + "x" + size.height);
        //设置预览 与 照片
        setParameters(previewSize, size.width, size.height);
        addCameraCallBack();
    }

    public void startPreview(SurfaceHolder surfaceHolder) {
        if (previewing)
            stopPreview();
        try {
            mCamera.setPreviewCallback(this);
            mCamera.setPreviewDisplay(surfaceHolder);
            mCamera.startPreview();
            previewing = true;
            Log.d(TAG, "startPreview");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void stopPreview() {
        if (mCamera != null && previewing) {
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);  // Camera is being used after Camera.release() was called
            previewing = false;
            Log.d(TAG, "stopPreview");
        }
    }

    private void setParameters(int pictureSize, int width, int height) {
        float rate = (float) width / height;//比例
        rate = Float.valueOf(String.format(Locale.getDefault(), "%.2f", rate));
        Log.i(TAG, "相机预览:" + width + "x" + height + "--" + pictureSize + " dpi----rate:" + rate + "---");
        Camera.Size size;
        if (pictureSize > 0) {
            size = calculateCameraFrameSize(mPictureSizes, pictureSize, rate);
        } else {
            size = mPictureSizes.get(mPictureSizes.size() - 1);
        }
        mParameters.setPictureSize(size.width, size.height);
        Log.i(TAG, "设置图片:w = " + size.width + "x" + size.height + "--rate:" + (float) size.width / size.height);

        size = calculateCameraFrameSize(mPreviewSizes, pictureSize, rate);
        mParameters.setPreviewSize(size.width, size.height);
        Log.i(TAG, "设置预览:w = " + size.width + "x" + size.height + "--rate:" + (float) size.width / size.height);

        mParameters.setPreviewFormat(ImageFormat.NV21);

        List<String> FocusModes = mParameters.getSupportedFocusModes();
        if (FocusModes != null && FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
            mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        }
        //mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//连续对焦

        //以下两句 影响 小米5 自动对焦
//        mParameters.setSceneMode(Camera.Parameters.SCENE_MODE_STEADYPHOTO);
//        mParameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);

        try {
            mCamera.setParameters(mParameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void addCameraCallBack() {
        Camera.Parameters params = mCamera.getParameters();
        int mFrameWidth = params.getPreviewSize().width;
        int mFrameHeight = params.getPreviewSize().height;

        int pxSize = mFrameWidth * mFrameHeight;
        pxSize = pxSize * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8;
        mBuffer = new byte[pxSize];

        mCamera.addCallbackBuffer(mBuffer);
        mCamera.setPreviewCallbackWithBuffer(this);

        if (mListener != null)
            mListener.onCamera(mCamera);
    }

    protected Camera.Size calculateCameraFrameSize(List<Camera.Size> supportedSizes, int previewSize, float rate) {
        int k = 0;
        int calcWidth = 0;
        int calcHeight = 0;
        for (int i = 0; i < supportedSizes.size(); i++) {
            Camera.Size size = supportedSizes.get(i);
            int width = size.width;
            int height = size.height;
            if (equalRate(size, rate)) {
                Log.i(TAG, "CameraSize:" + width + "x" + height + "--rate:" + (float) width / height);
                //1088 这个像素容易出错
                if (width >= calcWidth && height >= calcHeight && height <= previewSize && height != 1088) {//960
                    calcWidth = width;
                    calcHeight = height;
                    k = i;
                }
            }
        }
        return supportedSizes.get(k);
    }

    /**
     * 比例
     *
     * @param size
     * @param rate 16:9 1.777777
     * @return
     */
    private boolean equalRate(Camera.Size size, float rate) {
        if (rate <= 0)
            return true;
        return (double) Math.abs((float) size.width / (float) size.height - rate) <= 0.2D;
    }

    public void releaseCamera() {
        synchronized (this) {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.setPreviewCallback(null);
                mCamera.release();
            }
            mCamera = null;
            if (mListener != null)
                mListener.onReleaseCamera();
        }
    }

    /**
     * 聚焦
     */
    public void doAutoFocus() {
        try {
            mCamera.cancelAutoFocus();
            parameters = mCamera.getParameters();
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
            mCamera.setParameters(parameters);
            mCamera.autoFocus(new Camera.AutoFocusCallback() {
                @Override
                public void onAutoFocus(boolean success, Camera camera) {
                    if (success) {
                        camera.cancelAutoFocus();// 只有加上了这一句,才会自动对焦。
                        if (!Build.MODEL.equals("KORIDY H30")) {
                            parameters = camera.getParameters();
                            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);// 1连续对焦
//                            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
                            camera.setParameters(parameters);
                        } else {
                            parameters = camera.getParameters();
                            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                            camera.setParameters(parameters);
                        }
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized boolean isOpen() {
        return mCamera != null;
    }

    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        if (mListener != null)
            mListener.onPreviewFrame(bytes, camera);
        if (mCamera != null)
            mCamera.addCallbackBuffer(mBuffer);
    }

    public interface OnCameraManagerListener {
        void onCamera(Camera camera);

        void onReleaseCamera();

        void onPreviewFrame(byte[] bytes, Camera camera);
    }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,563评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,694评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,672评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,965评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,690评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,019评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,013评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,188评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,718评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,438评论 3 360
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,667评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,149评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,845评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,252评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,590评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,384评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,635评论 2 380