[Android] 안드로이드 SurfaceView 카메라 연동하기
안드로이드로 카메라를 연동 및 제어 방법입니다.
소스코드는 주요소스코드만 첨부합니다.
설명은 주석을 참고하세요.
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을 사용해 자동차 번호판 인식 어플리케이션을 제작해보겠습니다.
* 참고 글
'Android' 카테고리의 다른 글
안드로이드 이미지 뷰 여백 없애기 (0) | 2018.08.28 |
---|---|
안드로이드 카메라 줌 기능 구현 (1) | 2018.08.27 |
[안드로이드] 비트맵 이진화 처리 (1) | 2018.08.25 |
[안드로이드] 실시간 SurfaceView 화면 캡쳐하기 (1) | 2018.08.24 |
안드로이드 특수키 제어하기 (3) | 2018.08.23 |
[Android] 안드로이드 내부 저장소 텍스트 파일 읽기 및 쓰기 (2) | 2018.08.21 |
안드로이드 EditText 사용법 정리 (0) | 2018.08.20 |
[Android] 안드로이드 키보드 입력 창 올리기/ 내리기 (0) | 2018.08.20 |