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

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

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



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 이진화 처리






Android Studio에서 EditText 사용시 필요한 Methods에 대한 정리입니다.


1. ems : 최초 기본 너비 설정

사용 : android:ems="8"


2. maxLength : text 최대 길이 설정

사용 : android:maxLength="12"


3. textColor : text 색상 설정

사용 : android:textColor="#000000"


4. lines : 줄 개수 설정(최초 사이즈 확보)

사용 : android:lines=1


5. maxLines : 줄 최대 개수 설정(최초 사이즈1)

사용 : android:maxLines=2


6. singleLine : 한줄만 사용

사용 : android:singleLine="true"


7. background : 배경 설정

* 테두리있는 EditText 만들기

- xml파일 생성(layer-list)

- 다음과 같이 입력


<item>

<shape android:shape="rectangle">

<stroke android:width="1dp" android:color="FF948B8B"/>

<solid android:color="#FFFFFFFF"/>

</shape>

</item>


- 해당 파일을 editText에서 android:background="@drawble/파일명"으로 설정


8. capitialize : 자동 대문자 변경

사용 : android:capitialize="속성"

속성값 : 

characters - 모든 글자 대문자로 변경

words - 각 단어의 첫번째 글자를 대문자로 변경

sentences : 각 문장의 첫번째 단어를 대문자로 변경

none - 사용안함


9. hint : EditText가 비어있는 상태에서 출력 될 내용

사용 : android:hint="텍스트를 입력하세요."





InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); // 키보드 객체 받아오기


imm.hideSoftInputFromWindow(input_text.getWindowToken(), 0 ); // 키보드 숨기기


imm.showSoftInput(input_text, 0); // 키보드 보이기




InputMethodManager를 선언하고

키보드 입력 창을 보이고 싶은곳에 imm.showSoftInput을 입력

*parameter 1 : input_text 는 포커스를 받을 edit_text object 명

*parameter 2 : 0, HIDE_NOT_ALWAYS, HIDE_IMPLICIT_ONLY


키보드 입력 창을 숨기고 싶은 곳에 imm.hideSoftInputFromWindow를 입력

*parameter 1 : View.getWindowToken() : Request 받을 window token

*parameter 2 : flags : 0, HIDE_NOT_ALWAYS, HIDE_IMPLICIT_ONLY


to Top