1. 코드는 출처를 밝혀주신다면 어디든 퍼가도 상관없습니다.


2. 코드에 궁금한점이 있다면 댓글로 작성해 주신다면 가능한 답변해 드립니다.


3. 저도 공부하는 학생이라 모르는게 많습니다. 그래서 자세한 답변을 해드릴 수 없습니다.


블로그의 모든 코드는 제 GitHub에 있습니다.


GitHub : https://github.com/pchan1401-ICIL



이전에 있던 Camera2 프리뷰 기능에 캡쳐기능을 추가하였다.


우선 코드는 책(빛과 소리의 향연 안드로이드 프로그래밍, 박헌재)의 코드를 사용하였다.


코드가 간단할 뿐만 아니라, 블로그에 있던 코드와 거의 흡사하여 적용이 용이하다.


기존의 코드에서 takePicture함수만 추가되었다.


코드 설명에 앞서 Camera2의 특징은 출력을 여러가지로 할 수 있다.


하나의 카메라의 출력을 프리뷰, 사진, 동영상으로 전송할 수 있다.


각각의 출력을 surface라고 한다.


여러 surface를 지정하여 다른 해상도의 사진을 동시에 촬영 가능하다.


또한 동영상을 촬영하는 동안 사진을 저장할 수 있다.


그럼 코드를 보자


protected void takePicture() {
if(null == mCameraDevice) {
Log.e(TAG, "mCameraDevice is null, return");
return;
}

try {
Size[] jpegSizes = null;
if (map != null) {
jpegSizes = map.getOutputSizes(ImageFormat.JPEG);
}
int width = 640;
int height = 480;
if (jpegSizes != null && 0 < jpegSizes.length) {
width = jpegSizes[0].getWidth();
height = jpegSizes[0].getHeight();
}

ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
List<Surface> outputSurfaces = new ArrayList<Surface>(2);
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(new Surface(mTextureView.getSurfaceTexture()));

final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(reader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);

// Orientation
int rotation = ((Activity)mContext).getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));

final File file = new File(Environment.getExternalStorageDirectory()+"/DCIM", "pic.jpg");

ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
save(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (image != null) {
image.close();
reader.close();
}
}
}

private void save(byte[] bytes) throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(file);
output.write(bytes);
} finally {
if (null != output) {
output.close();
}
}
}
};

HandlerThread thread = new HandlerThread("CameraPicture");
thread.start();
final Handler backgroudHandler = new Handler(thread.getLooper());
reader.setOnImageAvailableListener(readerListener, backgroudHandler);

final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request, TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Toast.makeText(mContext, "Saved:"+file, Toast.LENGTH_SHORT).show();
startPreview();
}

};

mCameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
session.capture(captureBuilder.build(), captureListener, backgroudHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

@Override
public void onConfigureFailed(CameraCaptureSession session) {

}
}, backgroudHandler);

} catch (CameraAccessException e) {
e.printStackTrace();
}
}


중간에 outputSurface를 설정하여 이곳에서 출력을 사진과 프리뷰를 설정하였다.


사진을 찍는 동시에 프리뷰를 보는 동작을 하기 위해서이다.


찍은 사진은 ImageReader클래스를 이용하여 출력을 한다.


ImageReader에서 출력된 사진은 JPEG 형식으로 저장된다.



좀 더 복잡한 코드는 다음과 같다.


Camera2Basic : https://github.com/googlesamples/android-Camera2Basic


mCaptureCallback을 이용하여 최적화가 잘 되어있다. 



GitHub : https://github.com/pchan1401-ICIL/picture2.git









기존의 Camera API는 카메라를 카메라로 보았다면


Android 5.0이상에서는 카메라는 센서처럼 취급한다.


camera2 api 강좌 : http://myandroidarchive.tistory.com/1


우선 카메라의 화각 계산 공식은 아래 사이트에서 볼 수 있다.


Calculation Field of View : http://www.bobatkins.com/photography/technical/field_of_view.html


이전 코드와 변경사항은 아래 코드가 추가되었다는 점이다.


float horizonalAngle;
float verticalAngle;

private void calculateFOV(CameraManager cManager) {
try {
for (final String cameraId : cManager.getCameraIdList()) {
CameraCharacteristics characteristics = cManager.getCameraCharacteristics(cameraId);
int cOrientation = characteristics.get(CameraCharacteristics.LENS_FACING);
if (cOrientation == CameraCharacteristics.LENS_FACING_BACK) {
float[] maxFocus = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
SizeF size = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE);
float w = size.getWidth();
float h = size.getHeight();
horizonalAngle = (float) (2*Math.atan(w/(maxFocus[0]*2)));
verticalAngle = (float) (2*Math.atan(h/(maxFocus[0]*2)));
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

먼저, LENS_INFO_AVAILABLE_FOCUS_LENGTHS는 카메라가 사용가능한 렌즈의 초점거리를 얻기 위하여 사용하였다.


다음으로 SENSOR_INFO_PHYSICAL_SIZE는 센서의 크기를 불러오기 위해 사용하였다.


이 값을 이용하여 수직, 수평 FOV각도 값을 구할 수 있다.


FOV (rectilinear) =  2 * arctan (frame size/(focal length * 2))



GitHub : https://github.com/pchan1401-ICIL/Camera2FOV

+ Recent posts