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을 더욱 효과적으로 처리할 수 있습니다.



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




어플리케이션 제작 중에 SurfaceView에 CameraPreview를 이용하여 카메라를 제어하는 프로그래밍 중

실시간으로 SurfaceView를 캡쳐해야하는 코드가 필요해 처리하는 과정 등을 정리하여 포스팅합니다.

SurfaceView는 일반적인 함수로 캡쳐하면 검은 화면만 출력되기 때문에...

여러가지 찾아본 후 적용되는 코드로 설명하곘습니다.

CameraPreview로 SurfaceView에 카메라 화면을 출력할 때, 실시간으로 사진파일이 필요할 때 사용합니다.


[CameraPreview.java]


public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback {

... 중략 ...

@Override

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

... 중략 ...

mCamera.setPreviewDisplay(mHolder);

mCamera.setPreviewCallback(new Camera.PreviewCallback(){

@Override

public void onPreviewFrame(byte[] data, Camera camera) {

// 현재 SurfaceView를 JPEG Format으로 변경

Camera.Parameters parameters = camera.getParameters();

int w = parameters.getPreviewSize().width;

int h = parameters.getPreviewSize().height;

int format = parameters.getPreviewFormat();

YuvImage image = new YuvImage(data, format, w, h, null);

ByteArrayOutputStream out = new ByteArrayOutputStream();

Rect area = new Rect(0, 0, w, h);

image.compressToJpeg(area, 100, out);

Bitmap bm = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());

byte[] currentData = out.toByteArray();         

                   

... 중략 ...

}

}

}


기본 코드입니다.

CameraView class에서 SurfaceHolder.Callback을 implements하고

surfaceChanged() 에서 mCamera.setPreviewCallback을 호출합니다.

onPreviewFrame 메소드는 CameraPreview에서 실시간으로 호출되는 함수입니다.

위 코드는 현재 SurfaceView 화면을 캡쳐해서 JPEG Format으로 만들어주는 코드입니다.


여기서 JPEG 파일을 PNG 파일로 바꾸는 방법입니다.


... 위 코드로 부터 계속 ...

int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);

Matrix matrix = new Matrix();

matrix.postRotate(orientation);


BitmapFactory.Options options = new BitmapFactory.Options();

options.inSampleSize = 1 // 이미지 사이즈 리스케일(2의 지수 승으로 해야 처리속도 빠름; 1,2,4,8,16...; 필요시)

options.inPreferredConfig = Bitmap.Config.ARGB_8888;

// RGB_565 : 16bit Bitmap, ARGB_8888 : 32bit Bitmap, 4444는 저품질이라 추천안함


Bitmap bitmap = BitmapFactory.decodeByteArray(currentData, 0, currentData.length, options);

bitmap =  Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);

// bitmap src, start x, start y, width, height, 회전, Flag


 // Bitmap 이미지를 surfaceView 좌표로 변환

bitmap = GetBinaryBitmap(bitmap); // bitmap 이진화 처리


//bitmap을 byte array로 변환

 ByteArrayOutputStream stream = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); // 100 수치 변경해서 품질(Quality) 변경

currentData = stream.toByteArray();


// 이후 해당 Byte Array로 파일 저장 및 처리...

... 중략 ....

}

}

}


조금 복잡할 수도 있는 코드입니다.

한줄씩 주석 포함해서 해석하면서 코드 작성하시면 쉽게 처리 가능할 것 같습니다.


PNG(Bitmap으로) 파일 저장하는 방법은 Google에 많이 있고 또 예제코드가 있기 때문에 생략하겠습니다.


이상 CameraPreview에서 카메라 화면을 SurfaceView에 출력하고 그 화면을 캡쳐하는 방법에 대해 알아보았습니다.

질문 사항은 댓글로 남겨주세요!!


* 참고 글 : 

SurfaceView CameraPreview 연동

Bitmap 이진화 처리



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

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

먼저 실행 될 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()로 객체를 꼭 닫아줍니다.







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

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







to Top