Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> 3D圖片采集與展示(SurfaceView 自適應 Camera, 錄制視頻, 抽取幀),

3D圖片采集與展示(SurfaceView 自適應 Camera, 錄制視頻, 抽取幀),

編輯:關於android開發

3D圖片采集與展示(SurfaceView 自適應 Camera, 錄制視頻, 抽取幀),


最近在做一個3D圖片采集與展示。

主要功能為:自定義Camera(google 已經擯棄了Camera, 推薦使用Camera2,後續篇幅,我將會用Camera2取代Camera),圍繞一個物體360度錄制一個視頻,然後在該視頻抽取一定數量的幀,保存為圖片存放。最後在一個Activity頁面展示第一張圖片,通過滑動或點擊切換下一張圖片,從而形成用圖片展示的3D效果。該項目主要的目的是采集3D圖片素材,然後上傳到服務器處理,最終在用戶客戶端或網頁端展示是通過OpenGL ES處理而來。

技術要點:

    1.在SurfaceView 展示Camera的時候,如果不按照camera支持的尺寸比例,那麼預覽會出現拉伸。   

mCamera.getParameters().getSupportedPreviewSizes();
mCamera.getParameters().getSupportedPictureSizes();

可以通過debug查看該攝像頭支持哪些預覽框大小、圖片大小。如果要做適配,需要通過輪詢獲取自己所需要范圍大小。通常情況下,是支持全屏大小,也就是該手機的像素尺寸。
如果你想把預覽框設置成正方形,原則上是不行的(本人目前沒有找到相應的方法,求大牛指示),那我們可以先用全屏的攝像框尺寸,然後通過在層疊隱藏部分區域形成正方形,
再獲取圖像時根據隱藏的區域做相應的截圖,就能得到你想要的任何效果了。(如上圖示例)

在預覽中也有些小問題,比如說Camera默認是橫向取景,你需要 mCamera.setDisplayOrientation(90); 同時圖片保存的時候需要再旋轉90度,才能達到預覽的效果。

2.在錄像時,也會遇到一些問題圖片會有拉伸,跟預覽的時候不一致。這時,應該獲取設備所支持的參數。

CamcorderProfile mProfile = null;
if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);

}else if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
}else {
mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
}
主要還是要根據自己的實際情況去獲取,然後再設置參數:

mMediaRecorder.setProfile(mProfile);//不知道為什麼,直接這麼設置,不起作用,要下面set 才行

//mMediaRecorder.setProfile(mProfile);//不知道為什麼,直接這麼設置,不起作用,要下面set 才行
mMediaRecorder.setOutputFormat(mProfile.fileFormat);// 視頻輸出格式
mMediaRecorder.setVideoFrameRate(mProfile.fileFormat);// 設置錄制的視頻幀率
mMediaRecorder.setVideoSize(mHeight, mWidth);;// 設置分辨率:
mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);// 設置幀頻率,然後就清晰了
mMediaRecorder.setVideoEncoder(mProfile.videoCodec);// 視頻錄制格式

3. 從視頻抽幀, 每一幀是bitmap形式返回。從Java接口提供的抽幀方法,

MediaMetadataRetriever 類
getFrameAtTime(long timeUs)
通過時間去抽幀,而每一幀的時間也未必相隔是均勻的,這導致抽出來的幀會有重復,這需要再深入研究(後續再研究)。
具體方法為:

/**
 * 獲取視頻關鍵幀
 */
public void getFrameFromVideo(String filePath, String dirName){

    mFile = new File(filePath);
    retriever = new MediaMetadataRetriever();
    retriever.setDataSource(filePath);
    fileLength = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);

    long lengthLong = Long.parseLong(fileLength) *1000/ (Constants.Num_Frame - 1);
    String prefixName = Utils.randomCapital(5);//圖片名前綴
    int k = 0;
    for (long i = 0; i< Long.parseLong(fileLength)*1000+lengthLong; i=i+lengthLong){
        k++;
        Bitmap bitmap = retriever.getFrameAtTime(i);
        if(bitmap != null){
            Matrix matrix = new Matrix();
            matrix.reset();
            matrix.setRotate(90);//圖片默認是橫盤的,轉90度變豎屏
            if(bitmap.getWidth() >bitmap.getHeight()){
                bitmap = Bitmap.createBitmap(bitmap,(bitmap.getWidth() - bitmap.getHeight())/2,0, bitmap.getHeight(), bitmap.getHeight(),matrix, true);
            }else{
                bitmap = Bitmap.createBitmap(bitmap,0,(bitmap.getHeight()-bitmap.getWidth())/2, bitmap.getWidth(), bitmap.getWidth(),matrix, true);
            }
            BitmapUtils.saveBitmap(bitmap, dirName, prefixName + k + ".jpg");
        }
        if(k < 50){
            sendMessageForProgress(dirName,k,true);
        }else {
            sendMessageForProgress(dirName,k,false);
        }
    }
    //刪除視頻文件
    mFile.delete();
}

  

最後呈現一下,預覽與錄制視頻的代碼吧。RecordVideoActivity

public class RecordVideoActivity extends AppCompatActivity implements View.OnClickListener
                             , SurfaceHolder.Callback, MediaRecorder.OnErrorListener{

    private ProgressBar mProgressbar;
    private SurfaceView mSurfaceView;
    private MediaRecorder mMediaRecorder;// 錄制視頻的類
    private SurfaceHolder mSurfaceHolder;
    private Camera mCamera;
    private Timer mTimer;// 計時器
    private boolean isOpenCamera = true;// 是否一開始就打開攝像頭
    private final static int mRecordMaxTime = 20;// 一次拍攝最長時間
    private OnRecordFinishListener mOnRecordFinishListener;// 錄制完成回調接口
    private int mTimeCount;// 時間計數
    private File mVecordFile = null;// 文件
    private int mWidth = 0;// 視頻分辨率寬度
    private int mHeight = 0;// 視頻分辨率高度
    private boolean isStarting = false;

    @Override

    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉標題欄
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);// 設置全屏
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_record_video);
        initView();
    }

    private void initView() {
        mProgressbar = (ProgressBar) findViewById(R.id.progressBar);
        mProgressbar.setMax(mRecordMaxTime);
        findViewById(R.id.btn_start).setOnClickListener(this);
        mSurfaceView = (SurfaceView)findViewById(R.id.surfaceView);
        mSurfaceHolder = mSurfaceView.getHolder();// 取得holder
        mSurfaceHolder.addCallback(this); // holder加入回調接口
        mSurfaceHolder.setKeepScreenOn(true);
    }

    /**
     * 初始化攝像頭
     */
    private void initCamera(int width, int height) {
        if (mCamera != null) {
            freeCameraResource();
        }
        try {
            mCamera = Camera.open();
            if (mCamera == null)
                return;

            mCamera.setDisplayOrientation(90);//攝像頭默認是橫向,需要調整角度變成豎直
            mCamera.setPreviewDisplay(mSurfaceHolder);
            Camera.Parameters parameters = mCamera.getParameters();// 獲得相機參數
            mWidth = width;
            mHeight = height;

            parameters.setPreviewSize(height, width); // 設置預覽圖像大小
            parameters.set("orientation", "portrait");
            List<String> focusModes = parameters.getSupportedFocusModes();
            if (focusModes.contains("continuous-video")) {
                parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
            }
            mCamera.getParameters().getSupportedPreviewSizes();
            mCamera.getParameters().getSupportedPictureSizes();

            mCamera.setParameters(parameters);// 設置相機參數
            mCamera.startPreview();// 開始預覽
            mCamera.unlock();//解鎖,賦予錄像權限

        } catch (Exception e) {
            e.printStackTrace();
            freeCameraResource();
        }
    }

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

    /**
     * 釋放攝像頭資源
     */
    private void freeCameraResource() {
        if (mCamera != null) {
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.lock();
            mCamera.release();
            mCamera = null;
        }
    }

    /**
     * 錄制前,初始化
     */
    private void initRecord() {
        try {
            mMediaRecorder = new MediaRecorder();
            mMediaRecorder.reset();
            if(mCamera != null)
                mMediaRecorder.setCamera(mCamera);
            mMediaRecorder.setOnErrorListener(this);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);// 視頻源

            CamcorderProfile mProfile = null;
            if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
                mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);

            }else if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
                mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
            }else {
                mProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
            }
//            mMediaRecorder.setProfile(mProfile);//不知道為什麼,直接這麼設置,不起作用,要下面set 才行
            mMediaRecorder.setOutputFormat(mProfile.fileFormat);// 視頻輸出格式
            mMediaRecorder.setVideoFrameRate(mProfile.fileFormat);// 設置錄制的視頻幀率
            mMediaRecorder.setVideoSize(mHeight, mWidth);;// 設置分辨率:
            mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate);// 設置幀頻率,然後就清晰了
            mMediaRecorder.setVideoEncoder(mProfile.videoCodec);// 視頻錄制格式
//            }

            mMediaRecorder.setOutputFile(mVecordFile.getAbsolutePath());

            mMediaRecorder.prepare();
            mMediaRecorder.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 開始錄制視頻
     */
    public void startRecord(final OnRecordFinishListener onRecordFinishListener) {
        isStarting = true;
        this.mOnRecordFinishListener = onRecordFinishListener;
        createRecordDir();
        try {
            initRecord();
            mTimeCount = 0;// 時間計數器重新賦值
            mTimer = new Timer();
            mTimer.schedule(new TimerTask() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    mTimeCount++;
                    mProgressbar.setProgress(mTimeCount);// 設置進度條
                    if (mTimeCount == mRecordMaxTime) {// 達到指定時間,停止拍攝
                        stop();
                        if (mOnRecordFinishListener != null)
                            mOnRecordFinishListener.onRecordFinish();
                    }
                }
            }, 0, 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 停止拍攝
     */
    public void stop() {
        stopRecord();
        releaseRecord();
        freeCameraResource();

    }

    /**
     * 停止錄制
     */
    public void stopRecord() {
        mProgressbar.setProgress(0);
        if (mTimer != null)
            mTimer.cancel();
        if (mMediaRecorder != null) {
            // 設置後不會崩
            mMediaRecorder.setOnErrorListener(null);
            mMediaRecorder.setPreviewDisplay(null);
            try {
                mMediaRecorder.stop();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (RuntimeException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 釋放資源
     */
    private void releaseRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null);
            try {
                mMediaRecorder.release();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        mMediaRecorder = null;
    }

    /**
     * 創建目錄與文件
     */
    private void createRecordDir() {
        File FileDir = new File(Environment.getExternalStorageDirectory() + File.separator+"RecordVideo/");
        if (!FileDir.exists()) {
            FileDir.mkdirs();
        }
        // 創建文件
        try {
            mVecordFile = new File(FileDir.getAbsolutePath()+"/test.mp4");
            Log.d("Path:", mVecordFile.getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    OnRecordFinishListener recordFinishListener = new OnRecordFinishListener() {
        @Override
        public void onRecordFinish() {
            Intent intent = new Intent(RecordVideoActivity.this, MainActivity.class);
            intent.putExtra("filePath", mVecordFile.getAbsolutePath());
            setResult(Activity.RESULT_OK, intent);
            finish();
        }
    };

    @Override
    public void onBackPressed() {
       if(!isStarting){
           finish();
       }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_start:
                if(!isStarting)
                   startRecord(recordFinishListener);
                break;
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        initCamera(width,height);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        freeCameraResource();
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        try {
            if (mr != null)
                mr.reset();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 錄制完成回調接口
     */
    public interface OnRecordFinishListener {
         void onRecordFinish();
    }

}
   最後提供源代碼:https://github.com/xiaoxiaoqingyi/3DShow



  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved