SeekBar를 이용한 카메라 줌인, 줌아웃 기능 제어하는 방법입니다.


먼저 Layout입니다.


<SeekBar

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:id="@+id/zoom"

android:max="10" />


id는 Activity에서 사용할 id로 설정해주시면 되고, max는 최대 크기입니다. 10으로 설정 시 1~10까지 seekbar의 칸이 분리됩니다.


다음 Activity입니다.


ZoomseekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

@Override

public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

int Zoom = progress * 6;


if(CameraPreview.params.getMaxZoom() < Zoom) {

Zoom = CameraPreview.params.getMaxZoom();

}

CameraPreview.params.setZoom(Zoom);

CameraPreview.mCamera.setParameters(CameraPreview.params);

}


@Override

public void onStartTrackingTouch(SeekBar seekBar) { }

@Override

public void onStopTrackingTouch(SeekBar seekBar) { }

});


SeekBar의 값 변경 시 호출되는 onProgressChanged 함수에서 처리해주시면 됩니다.

휴대폰 기종마다 지원되는 줌 크기가 다르기 때문에

Parameter의 getMaxZoom() 함수를 통해 최대 줌 크기값을 받아 올 수 있습니다.

제 기종의 경우 MaxZoom 값이 60이 나오네요.

애초에 SeekBar의 max 크기를 10으로 주었기 때문에,

Zoom값은 progress * 6 으로 설정했습니다.


setZoom()을 통해 줌 값을 넘겨주고, 파라미터 설정을 해주면 CameraPreview에서 정상 작동하는것을 확인 할 수 있습니다.

 


참고 : 

안드로이드 SurfaceView 카메라 연동

안드로이드 SurfaceView 화면 캡쳐



정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.



비트맵 이진화 처리 함수입니다.

먼저 비트맵을 받아 복사한 후

모든 픽셀을 탐색하며 색깔을 변경해줍니다.(GetNewColor)

그리고 새로운 비트맵을 반환합니다.


private Bitmap GetBinaryBitmap(Bitmap bitmap_src) {

    Bitmap bitmap_new=bitmap_src.copy(bitmap_src.getConfig(), true);

    for(int x=0; x<bitmap_new.getWidth(); x++) {

        for(int y=0; y<bitmap_new.getHeight(); y++) {

            int color=bitmap_new.getPixel(x, y);

            color=GetNewColor(color);

            bitmap_new.setPixel(x, y, color);

        }

    }

    return bitmap_new;

}


GetNewColor 함수입니다.

GetColorDistance 함수를 통해 흰색과 검정색 중 가까운 색깔을 선택합니다.

BLACK 부분에 0.4를 곱해 BLACK 값을 높여서 좀 더 어두운 환경에서도 이진화를 잘 수행할 수 있도록 했습니다.

흰색 또는 검정색(이진화 값)을 반환합니다.


private int GetNewColor(int c) {

    double dwhite=GetColorDistance(c,Color.WHITE);

    double dblack=GetColorDistance(c,Color.BLACK)*0.4;

    if(dwhite<=dblack) {

        return Color.WHITE;

    }

    else {

        return Color.BLACK;

    }

}


GetColorDistance 함수입니다.

R,G,B 값을 받아와 제곱의 합의 제곱근입니다.(거리 구하는 공식)

거리 값을 반환합니다.


private double GetColorDistance(int c1, int c2) {

    int db= Color.blue(c1)-Color.blue(c2);

    int dg=Color.green(c1)-Color.green(c2);

    int dr=Color.red(c1)-Color.red(c2);

    double d=Math.sqrt(  Math.pow(db, 2) + Math.pow(dg, 2) +Math.pow(dr, 2)  );

    return d;

}


실행 결과입니다.


GetNewColor 함수의 BLACK 및 WHITE값을 적절히 조절하여 최적의 이진화 된 이미지를 얻을 수 있습니다.

이진화 된 이미지를 통해 OCR을 더욱 효과적으로 처리할 수 있습니다.



질문 및 오류사항은 댓글로 달아주세요.




안드로이드 프로그래밍에서 특수키 제어하는 방법입니다.

특수키란 키보드로 입력하는 키 외에 뒤로가기, 홈버튼, 볼륨버튼.. 등을 말합니다.

먼저 실행 될 activity에서 우클릭 - Generate를 눌러줍니다.



생성자, getter, setter...등 목록이 많이 있습니다.

이 중에 Override Methods를 선택합니다.



OnKeyDown이라고 검색해봅시다.(창이 뜬 상태에서 그냥 치면 검색됩니다.)

없는 경우 이미 메소드를 생성한 경우입니다.



OK버튼을 누르면 아래와 같은 코드가 생깁니다.

@Override라는 문구가 싫으신 분은 Insert @Override 체크버튼을 해제하시면 됩니다.



자 이제 이 메소드에서 특수키 이벤트를 구현하시면 됩니다.

다음은 특수키 목록입니다.


KEYCODE_BACK : 뒤로가기 버튼

KEYCODE_VOLUME_DOWN : 볼륨 ↓ 버튼

KEYCODE_VOLUME_UP : 볼륨 ↑ 버튼

KEYCODE_HOME : 홈 버튼


이외에 키보드에서 입력할 수 있는 모든 키는 코드로 정의되어있습니다.

이번시간에는 특수키 제어만 해보겠습니다.


다음과 같이 코드를 작성합니다.


public boolean onKeyDown(int keyCode, KeyEvent event) {

switch(keyCode){

case KeyEvent.KEYCODE_BACK :

// 여기에 뒤로가기 버튼을 눌렀을 때 행동 입력

break;

case KeyEvent.KEYCODE_VOLUME_DOWN :

// 여기에 볼륨 ↓ 버튼을 눌렀을 때 행동 입력

break;

case KeyEvent.KEYCODE_VOLUME_UP

// 여기에 볼륨 ↑ 버튼을 눌렀을 때 행동 입력

break;

case KeyEvent.KEYCODE_HOME

// 여기에 홈 버튼을 눌렀을 때 행동 입력

break;

}

return super.onKeyDown(keyCode, event);

}


필요한 키 코드만 복사해서 붙여넣어 사용하면됩니다.

단순히 키 입력을 막고싶다 하시는 분은


case KeyEvent.KEYCODE_BACK :

return;


라고 입력하시면 됩니다.



* 수정(8/24)

HOME키 이벤트는 위 코드로 제어할 시 제대로 작동이 안됩니다.

따라서 다음과 같이 처리합니다.

우클릭 - Generate - Override Methods 선택 후 onUserLeaveHint 검색하고 생성합니다.



휴대폰의 홈 키 또는 작업탭 키를 눌렀을 때 작동하는 것을 확인했습니다.



저는 Log로 작성했지만 홈 키 또는 작업탭 키를 눌렀을 때 작동 할 코드를 Log부분에 작성하시면 됩니다.




* 참고 : KeyEvent는 해당 코드를 입력한 Activity(화면)에서만 발생합니다.


* 참고 (더 많은 키 코드를 확인하고싶은 분은  아래 링크로 가서 확인하세요)

https://developer.android.com/reference/android/view/KeyEvent



오타 및 질문사항은 댓글로 달아주세요!!







Android 내부 저장소에 텍스트 파일(.txt) 읽기 및 쓰기 방법입니다.

/////////////////////// 파일 쓰기 ///////////////////////
String str = input_text.getText().toString();
// 파일 생성
File saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/camdata"); // 저장 경로
// 폴더 생성
if(!saveFile.exists()){ // 폴더 없을 경우
saveFile.mkdir(); // 폴더 생성
}
try {
long now = System.currentTimeMillis(); // 현재시간 받아오기
Date date = new Date(now); // Date 객체 생성
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTime = sdf.format(date);

BufferedWriter buf = new BufferedWriter(new FileWriter(saveFile+"/CarnumData.txt", true));
buf.append(nowTime + " "); // 날짜 쓰기
buf.append(str); // 파일 쓰기
buf.newLine(); // 개행
buf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
1) File 객체를 생성합니다.

2) Environment.getExternalStorageDirectory().getAbsolutePath() 까지 현재 경로이며 사용할 폴더를 하나 생성해줍니다.

3) 폴더가 없을 경우 mkdir()을 통해 생성해줍니다.

4) BufferedWriter를 통해 파일을 엽니다. 두번째 parameter를 true로 함으로써 파일을 이어서 쓰도록 합니다.

5) 사용 후 close()로 객체를 꼭 닫아줍니다.

/////////////////////// 파일 읽기 ///////////////////////
// 파일 생성
String line = null; // 한줄씩 읽기
File saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/camdata"); // 저장 경로
// 폴더 생성
if(!saveFile.exists()){ // 폴더 없을 경우
saveFile.mkdir(); // 폴더 생성
}
try {
BufferedReader buf = new BufferedReader(new FileReader(saveFile+"/CarnumData.txt"));
while((line=buf.readLine())!=null){
tv.append(line);
tv.append("\n");
}
buf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

1) File 객체를 생성합니다. 2) Environment.getExternalStorageDirectory().getAbsolutePath() 까지 현재 경로이며 사용할 폴더를 하나 생성해줍니다. 3) 폴더가 없을 경우 mkdir()을 통해 생성해줍니다. 4) BufferedReader 를 통해 파일을 엽니다.


5) while문을 이용하여 파일을 한줄씩 읽어옵니다. 5) 사용 후 close()로 객체를 꼭 닫아줍니다.







정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.








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

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

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



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