Android - HDR Camera
Здравствуйте уважаемые читатели. Как уже многие из вас знают, в iOS 4.1 появилась новая “фишка” - HDR Photo. Вещь крайне спорная, но довольно интересная. Вот и загорелся я сделть то же и на Android. В Android Market’е конечно же есть программы с похожим функционалом, но нам ведь интересно сделать самим, верно? Итак, приступим. Для начала стоит прочитать мою первую статью “Android: Hello World!” дабы лучше понимать материал.
Для начала нам нужно сделать обычную камеру, а затем добавить в нее обработку HDR. Создадим внешний вид приложения: на AbsoluteLayout положим SurfaceView, а на него - CheckBox и Button. Все это должно выглядеть примерно так:
Теперь давайте перейдем в режим правки XML и заменим тег SurfaceView на com.iorange.hdrcam.PreviewSurface (у вас название класса может отличаться). PreviewSurface будет у нас основным классом логики камеры, а так же на него будет выводиться Preview камеры.
public class PreviewSurface extends SurfaceView implements SurfaceHolder.Callback |
Этот класс должен реализовывать три метода: surfaceChanged, surfaceCreated и surfaceDestroyed. Так же этот Surface нужно зарегистрировать на получение сообщений в SurfaceHolder’e.
public PreviewSurface(Context context, AttributeSet attrs) { super(context, attrs); m_Holder = getHolder(); m_Holder.addCallback(this); m_Holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } |
Для доступа к камере нам нужно заявить о своих намерениях в манифесте. Для этого добавьте в manifest сразу за application следующие теги:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
Последний тег нужен для записи файлов на SD-карточку. Для доступа к Camera API есть класс android.hardware.Camera. Документация по нему есть на сайте разработчиков Android - http://developer.android.com/reference/android/hardware/Camera.html.
Для начала создадим объект класса Camera вызовом статического метода Camera.open(); Лучше всего это сделать в методе surfaceCreated.
@Override public void surfaceCreated(SurfaceHolder holder) { m_Camera = Camera.open(); try { m_Camera.setPreviewDisplay(holder); } catch (IOException e) { e.printStackTrace(); m_Camera.release(); m_Camera = null; } } |
После создания камеры мы устанавливаем нашу поверхность (Surface) в качестве экрана предпросмотра методом setPreviewDisplay. Этот метод может бросаться исключениями - посему окружим его try-catch. Следуя хорошим традициям сразу же позаботимся об освобождении камеры - добавим в метод surfaceDestroyed код для освобождения камеры:
@Override public void surfaceDestroyed(SurfaceHolder holder) { m_Camera.stopPreview(); m_Camera.release(); m_Camera = null; } |
Отлично, камера инициализируется и корректно освобождается. Теперь нужно заставить камеру выводить изображение на нашу поверхность. Для этого в у класса Camera есть метод startPreview, однако вызывать его можно только если камере установлен SurfaceHolder требуемой поверхности (что мы сделали раньше в методе surfaceCreated) и сама поверхность полностью иниализирована и готова. То есть делать это лучше всего в методе surfaceChanged:
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Camera.Parameters parameters = m_Camera.getParameters(); List sizes = parameters.getSupportedPreviewSizes(); Size optimalSize = getOptimalPreviewSize(sizes, width, height); parameters.setPreviewSize(optimalSize.width, optimalSize.height); m_Camera.setParameters(parameters); m_Camera.startPreview(); m_CanDoPhoto = true; } |
Здесь мы узнаем у камеры список поддерживаемых ею размеров превью и подбиравем подходящий методом getOptimalPreviewSize (реализация которого была полностью содрана с Android SDK). И в конце-концов стартуем превью методом startPreview. Уже сейчас можно скомпилировать и запустить проект - мы будем видеть результат “зрения” нашей камеры.
Давайте теперь научим наше приложение делать снимки и сохранять их. Сначала нужно добавить обработчик нажатия кнопки “Photo!”. Сделаем это в нашем Activity:
btnPhoto.setOnClickListener(btnPhoto_OnClick); View.OnClickListener btnPhoto_OnClick = new View.OnClickListener() { public void onClick(View v) { m_View.TakePicture(); } }; |
Теперь реализуем метод TakePicture в нашем PreviewSurface.
public void TakePicture() { if (m_Camera != null && m_CanDoPhoto) { m_CanDoPhoto = false; m_Camera.takePicture(shutterCallback, rawCallback, jpegCallback); } } |
Метод takePicture принимает 3 колбека - shutterCallback, rawCallback и jpegCallback.
shutterCallback вызывается сразу после захвата кадра - здесь, например, можно проигрывать звук щелчка затвора.
rawCallback даст нам сырые (RAW) данные картинки - без обработок и сжатия (именно эти данные мы и будем использовать для HDR корректировки).
jpegCallback даст нам уже готовую JPEG-сжатую картинку.
Давайте для начала сделаем возможность простой съемки - без HDR корректировки. Для этого для первых 2-х коллбеков мы сделаем пустые заглушки и реализуем jpegCallback. В jpegCallback мы просто будем записывать пришедшую картинку на SD-карточку.
ShutterCallback shutterCallback = new ShutterCallback() { public void onShutter() { // TODO Do something when the shutter closes. } }; PictureCallback rawCallback = new PictureCallback() { public void onPictureTaken(byte[] _data, Camera _camera) { // TODO Do something with the image RAW data. } }; PictureCallback jpegCallback = new PictureCallback() { public void onPictureTaken(byte[] _data, Camera _camera) { String path = Environment.getExternalStorageDirectory().toString(); File file = new File(path, "MyTestPhoto.jpg"); try { FileOutputStream fOut = new FileOutputStream(file); fOut.write(_data); fOut.flush(); fOut.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } m_CanDoPhoto = true; m_Camera.startPreview(); } }; |
На этом пока остановимся. Продолжим в следующей части статьи.
Скачать исходный код к статье:
AndroidHDRCam.zip (1555)