안드로이드로 카메라를 연동 및 제어 방법입니다.

소스코드는 주요소스코드만 첨부합니다.

설명은 주석을 참고하세요.



1. AndroidManifest.xml 에 접근 권한 추가


<!-- 카메라 권한 -->

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.CAMERA" />


2. SnackBar로 접근 권한 설정


// 카메라 접근 권한

private static final int PERMISSIONS_REQUEST_CODE = 100;

String[] REQUEST_PERMISSIONS = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};

private static final int CAMERA_FACING = Camera.CameraInfo.CAMERA_FACING_BACK;

private SurfaceView surfaceView; // 카메라 Preview 출력

private CameraPreview mCameraPreview; // 카메라 Preview

private View mLayout; // SnackBar 사용


// 화면 켜진 상태 유지

getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);


// 최초 카메라 Preview가 켜졌을 때

if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){

     int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);

     int writeExternalStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);


     // 권한 허용 시

     if(cameraPermission == PackageManager.PERMISSION_GRANTED && writeExternalStoragePermission ==                    PackageManager.PERMISSION_GRANTED){

          startCamera(); // 카메라 Preview를 SurfaceView에 출력

     }

            

     // 권한 없을 시            

     else{         

          if(ActivityCompat.shouldShowRequestPermissionRationale(this, REQUEST_PERMISSIONS[0]) ||     

               ActivityCompat.shouldShowRequestPermissionRationale(this, REQUEST_PERMISSIONS[1])){        

               Snackbar.make(mLayout, "이 앱을 실행하려면 카메라와 외부 저장소 접근 권한이 필요합니다.",                   

                    Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener(){                   

                    @Override                   

                    public void onClick(View v) {                    

                       ActivityCompat.requestPermissions(CameraAcitivity.this, REQUEST_PERMISSIONS, PERMISSIONS_REQUEST_CODE);         

                    }                  

               }).show();            

          }             

          else{ // 사용자가 퍼미션 거부 한 적이 없는 경우 퍼미션 요청                  

               ActivityCompat.requestPermissions(this, REQUEST_PERMISSIONS, PERMISSIONS_REQUEST_CODE);         

          }         

     }      

} else{ // 카메라 미 지원시        

     final Snackbar snackbar = Snackbar.make(mLayout, "디바이스가 카메라를 지원하지 않습니다.", Snackbar.LENGTH_INDEFINITE);         

     snackbar.setAction("확인", new View.OnClickListener() {             

          @Override            

          public void onClick(View v) {                

               snackbar.dismiss();            

          }         

     });        

     snackbar.show();    

}



3. 카메라 플래시 제어


flash_btn = (Button)findViewById(R.id.flash_btn); // 플래시 버튼 이벤트        

flash_btn.setOnClickListener(new View.OnClickListener() {

     @Override

     public void onClick(View v) {

          flash_count++;

          if(flash_count > 1)

               flash_count = 0;

     

          if(flash_count==1) { // 플래시 켜기

               CameraPreview.params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);

               CameraPreview.mCamera.setParameters(CameraPreview.params);

               Toast.makeText(CameraAcitivity.this, "Camera Flash ON", Toast.LENGTH_SHORT).show();

          }

          else { // 플래시 끄기

               CameraPreview.params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);

               CameraPreview.mCamera.setParameters(CameraPreview.params);

               Toast.makeText(CameraAcitivity.this, "Camera Flash OFF", Toast.LENGTH_SHORT).show();

          }

     }

});


4. 화면에 사각형 그리기


class DrawOn extends View{ // 사각형 그리기

    public DrawOn(Context context){

        super(context);

    }


    @Override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);


        Paint pt = new Paint();

        pt.setColor(Color.GREEN);

        pt.setStrokeWidth(5);


        canvas.drawLine(100,350,100,400, pt);

        canvas.drawLine(100,350,200,350, pt);


        canvas.drawLine(520,350,620,350, pt);

        canvas.drawLine(620,350,620,400, pt);


        canvas.drawLine(100,600,100,650, pt);

        canvas.drawLine(100,650,200,650, pt);


        canvas.drawLine(520,650,620,650, pt);

        canvas.drawLine(620,650,620,600, pt);

    }

}


5. SurfaceHoder 인터페이스


@Override

public void surfaceCreated(SurfaceHolder holder) {

// SurfaceView 생성시 호출 : 카메라 프리뷰 화면 출력 및 크기 회전 설정

    try{

        mCamera = Camera.open(mCameraID);

    }

    catch (Exception e){

        Log.e(TAG, "Camera" + mCameraID + " is not availabe : " + e.getMessage());

    }


    Camera.CameraInfo cameraInfo = new Camera.CameraInfo();

    Camera.getCameraInfo(mCameraID, cameraInfo);


    mCameraInfo = cameraInfo;

    mDisplayOrientation = mActivity.getWindowManager().getDefaultDisplay().getRotation();


    int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);

    mCamera.setDisplayOrientation(orientation);

    

    mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();

    requestLayout();


    params = mCamera.getParameters();


    List<String> focusModes = params.getSupportedFocusModes();

    if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)){

        params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

        mCamera.setParameters(params);

    }


    try{

        mCamera.setPreviewDisplay(holder);

        mCamera.startPreview();

        Log.d(TAG, "CAMERA Preview Started");

    }

    catch (IOException e){

        Log.d(TAG, "Error setting camera preview" + e.getMessage());

    }

}


@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

// SurfaceView 의 크기가 변경될 때 호출

    if(mHolder.getSurface() == null){

        Log.d(TAG, "Preview surface does not exist");

        return;

    }


    try{    

        mCamera.stopPreview();

        Log.d(TAG, "Preview Stopped");

    }

    catch(Exception e) {

        Log.d(TAG, "Error stating camera preview : " + e.getMessage());

    }


    int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);

    mCamera.setDisplayOrientation(orientation);


    try{

        mCamera.setPreviewDisplay(mHolder);

        mCamera.startPreview();

        Log.d(TAG, "Camera preview started");

    }

    catch(Exception e){

        Log.d(TAG, "Error stating camrea preview : " + e.getMessage());

    }

}


@Override

public void surfaceDestroyed(SurfaceHolder holder) {

// SurfaceView 종료시 호출

    if(mCamera!=null){

        if(isPreview) 

            mCamera.stopPreview();

        mCamera.release();

        mCamera = null;

        isPreview = false;

    }

}


6. 이미지 저장하기


// 이미지 저장

private class SaveImageTask extends AsyncTask<byte[], Void, Void> {

@Override

protected Void doInBackground(byte[]... data) {

    FileOutputStream outStream = null;

    try {

        File path = new File (Environment.getExternalStorageDirectory().getAbsolutePath() + "/camtest");

        if (!path.exists()) {

            path.mkdirs();

        }

        String fileName = String.format("%d.jpg", System.currentTimeMillis());

        File outputFile = new File(path, fileName);


        outStream = new FileOutputStream(outputFile);

        outStream.write(data[0]);

        outStream.flush();

        outStream.close();


        Log.d(TAG, "onPictureTaken - wrote bytes: " + data.length + " to " + outputFile.getAbsolutePath());

        mCamera.startPreview();

        // 갤러리에 반영

        Intent mediaScanIntent = new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

        mediaScanIntent.setData(Uri.fromFile(outputFile));

        getContext().sendBroadcast(mediaScanIntent);


        try {

            mCamera.setPreviewDisplay(mHolder);

            mCamera.startPreview();

            Log.d(TAG, "Camera preview started.");

        } catch (Exception e) {

            Log.d(TAG, "Error starting camera preview: " + e.getMessage());

        }

    } catch (FileNotFoundException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

    return null;

    }

}



* 실행화면




다음번엔 제어한 카메라 화면과 Tesseract-OCR을 사용해 자동차 번호판 인식 어플리케이션을 제작해보겠습니다.


* 참고 글

SurfaceView 화면 캡쳐하기

Bitmap 이진화 처리





to Top