본문 바로가기

전생의 기억/영상처리

안드로이드 이미지 트레킹(Image tracking)

반응형


스마트폰의 gyro 센서를 이용한
이미지 좌표값 이동 기능
파노라마 이미지에 적용 시 vr 같은 느낌을 받을 수 있다.
(소스파일 첨부)

 


<결과 영상>


원리는 간단합니다.

1. 스마트폰 또는 태블릿에 내장된 자이로센서 또는 가속도 센서의 값을 불러온다.
2. 안드로이드의 View 클래스 안의 OnDraw 메소드를 오버라이딩 하여 재정의 한다.
3. OnDraw메소드에서 원본 이미지에 대하여 Bitmap 객체를 사용하여 파노라마 이미지의 일정 부분을 커팅 한다.
4. 커팅 한 이미지에 대해 Canvas 객체를 이용해 사용자의 화면에 맞추어 커팅 한 이미지를 그린다.
5. 마지막으로 센서 값에 따라서 그리는 부분의 시작 좌표 X, Y가 바뀌기 때문에, 좌표값이 바뀔 때마다 액티비티에 이미지를 새로 그리기 위해 onDraw 안에서 invalidate메소드를 사용한다.


문제점
가속도 센서로부터 받은 값을 X,Y의 좌표 이동에 값에 사용하였더니 예상 결과와 달리 X 축의 값 조절이 잘 되지 않는다.



해결 방법
구글에서 제공하는 cardBoard 앱 개발 관련 오픈 API 소스 코드에서 참조하여 기능을 다시 구현할 예정이다.






구현
우선, 이클립스에서 프로젝트를 생성한 후,
다음과 같이 MainActivity 에 SensorEventListener를  implements 해주었습니다.

public class MainActivity extends Activity implements SensorEventListener {


onCreate에는 다음과 같이 작성해주었습니다.

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
dis_wid = metrics.widthPixels;
dis_high = metrics.heightPixels;

//센서 매니저 얻기
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//자이로스코프 센서(회전)
mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
//엑셀러로미터 센서(가속)
accSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

setContentView(new SampleView(this));
}

다음은, onSensorChanged 라는 이벤트 핸들러를 정의합니다. 여기서 멤버 변수인 gyroX, Y, Z , 그리고 accelX,Y,ZValue에 센서의 값을 저장하였습니다.


public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;

if (sensor.getType() == Sensor.TYPE_GYROSCOPE) {
gyroY = Math.round(event.values[1] * 1000);
gyroX = Math.round(event.values[0] * 1000);
gyroZ = Math.round(event.values[2] * 1000);

}
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
accelXValue = (double) event.values[0];
accelYValue = (double) event.values[1];
accelZValue = (double) event.values[2];
}


이후에 리스너를 등록하는 부분과 리스너를 해제하는 소스에 대한 부분을 코딩해야 하지만, 생략하였습니다.
다음은, onCreate에서 setContext로 뿌려주었던 SampleView. 즉, 스마트폰 화면에 나타나는 부분과 관련된 소스입니다.



private static class SampleView extends View {

private Bitmap image; // 이미지
public SampleView(Context context) {
super(context);

// 그림 읽어들이기
Resources r = context.getResources();
image = BitmapFactory.decodeResource(r, R.drawable.pano3);

}

@Override protected void onDraw(Canvas canvas) {
double cpX = gyroX;
double cpY = gyroY;
double NsM =(dis_wid/dis_high);

int width = image.getWidth();
int height = image.getHeight();
int new_high = height/3;
int new_wid = (int)(new_high *NsM);


double accelXgamdo=0;
double accelYgamdo=0;
Bitmap New;
Rect new2;
accelXgamdo = accelXValue;
accelYgamdo = Math.abs(accelYValue)*10;


if(cpX>150 && i>64 )
{
i = (int)(i-accelXgamdo);//x좌표 움직여
//여기에 가속센서를 활용해서 움직임 정도를 정해줘야 함
//원본에서 가져올 x좌표의 기준
}

if(cpX<-150)
{
i = (int)(i+accelXgamdo);
if(i+(new_wid)>=width)
{
i=(int)(i-5-accelXgamdo); //에러
}
}
if(cpY<-150 && j>64 && accelYgamdo<30)
{
int j_c = j;
j = (int)(j-accelYgamdo);//x좌표 움직여
//여기에 가속센서를 활용해서 움직임 정도를 정해줘야 함
//원본에서 잘라오는 소스
}

if(cpY>150 && accelYgamdo<30)
{
j = (int)(j+accelYgamdo);
if(j+(new_high)-30>=height)//
{
j=(int)(j-10-accelYgamdo);
}
}
New = Bitmap.createBitmap(image,i,j,new_wid,new_high); //New에 image의 일정 부분을 가져옴

//화면 조절
//int newHeight;
//int newWidth;

new2 = new Rect(0,0,(int)(dis_high*NsM),(int)dis_high); //가져온 New의 이미지를 x배 확대시킴
canvas.drawBitmap(New, null,new2,null);
canvas.restore();
invalidate();
//배경 이미지를 그린다.

}
}

onDraw 메소드에서
맴버변수인 gyroX, gyroY, accelXValue, accelYValue 를 받아옵니다.
double cpX = gyroX;
double cpY = gyroY;
double accelXgamdo=0;
double accelYgamdo=0;

NsM이라는 변수는
onCreate에서
dis_wid = metrics.widthPixels;
dis_high = metrics.heightPixels;
이라는 부분에서 얻어진 사용하는 스마트폰의 화면 크기를 얻어온 부분이며,
이를 width/height를 하여 얻은 값입니다.

width와 height는 원본 이미지의 width와 height의 값을 가집니다.


New는 원본 이미지의 일정부분을 display크기에 맞추어 잘라와서 일정 부분의 이미지를 가지고 있게 됩니다.
new2는 해당 이미지를 스마트폰 크기에 맞추어주는 용도로 사용이 됩니다.
i와,j라는 변수는 원본 이미지에서 이미지를 자르는 시작 X좌표와 Y좌표(Left _Bottom)이며,
자이로센서값에 따라 if문을 사용하여 왼쪽,오른쪽,위,아래로 움직임을 감지하고 그에 따라 i,j값에 변화를 주게 됩니다.
움직이는 정도(감도)는 accelXValue, accelYValue인 가속도센서값을 이용하였으며, 값이 너무 작으므로 적절하게 *10정도를 주고, Math.abs함수를 이용하여 절대치 값을 얻어왔습니다.
i,j값이 정해진 상태에서 canvas.drawBitmap을 이용하여 오리지날 이미지의 일정영역을 그리게 됩니다.
이렇게 하면 자이로 센서값이 변함에 따라서 i,j값이 바뀌게 되고,

코드 마지막에 invalidate() 메소드를 사용함으로서 값이 변할 때 Activity를 계속 뿌려주도록 하면
마치 사진이 움직이는 것 처럼 보이게 됩니다.

추가

AndroidManifest.xml 파일에 해당 activity에 대하여 theme속성과 screenOrientation 속성을 주면 알림바가 사라지게 됩니다.
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:screenOrientation="landscape">


다음에는 이 기능에 쓰일 수 있는 파노라마 이미지를 카메라로 찍어서 만들고, 바로 가져올 수 있도록 하는 기능을 구현할 생각입니다.
반응형