Import goldfish HAL sources from devices/generic/goldfish
We import all code here so we can customize and keep in sync with what we do on the host side more easily.
This commit is contained in:
parent
ddea17253c
commit
23392b9732
196 changed files with 62159 additions and 0 deletions
65
android/camera/fake-pipeline2/Base.h
Normal file
65
android/camera/fake-pipeline2/Base.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file includes various basic structures that are needed by multiple parts
|
||||
* of the fake camera 2 implementation.
|
||||
*/
|
||||
|
||||
#ifndef HW_EMULATOR_CAMERA2_BASE_H
|
||||
#define HW_EMULATOR_CAMERA2_BASE_H
|
||||
|
||||
#include <system/window.h>
|
||||
#include <hardware/camera2.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
|
||||
/* Internal structure for passing buffers across threads */
|
||||
struct StreamBuffer {
|
||||
// Positive numbers are output streams
|
||||
// Negative numbers are input reprocess streams
|
||||
// Zero is an auxillary buffer
|
||||
int streamId;
|
||||
uint32_t width, height;
|
||||
uint32_t format;
|
||||
uint32_t dataSpace;
|
||||
uint32_t stride;
|
||||
buffer_handle_t *buffer;
|
||||
uint8_t *img;
|
||||
};
|
||||
typedef Vector<StreamBuffer> Buffers;
|
||||
|
||||
struct Stream {
|
||||
const camera2_stream_ops_t *ops;
|
||||
uint32_t width, height;
|
||||
int32_t format;
|
||||
uint32_t stride;
|
||||
};
|
||||
|
||||
struct ReprocessStream {
|
||||
const camera2_stream_in_ops_t *ops;
|
||||
uint32_t width, height;
|
||||
int32_t format;
|
||||
uint32_t stride;
|
||||
// -1 if the reprocessing stream is independent
|
||||
int32_t sourceStreamId;
|
||||
};
|
||||
|
||||
} // namespace android;
|
||||
|
||||
#endif
|
||||
286
android/camera/fake-pipeline2/JpegCompressor.cpp
Normal file
286
android/camera/fake-pipeline2/JpegCompressor.cpp
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "EmulatedCamera2_JpegCompressor"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <ui/GraphicBufferMapper.h>
|
||||
|
||||
#include "JpegCompressor.h"
|
||||
#include "../EmulatedFakeCamera2.h"
|
||||
#include "../EmulatedFakeCamera3.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
JpegCompressor::JpegCompressor():
|
||||
Thread(false),
|
||||
mIsBusy(false),
|
||||
mSynchronous(false),
|
||||
mBuffers(NULL),
|
||||
mListener(NULL) {
|
||||
}
|
||||
|
||||
JpegCompressor::~JpegCompressor() {
|
||||
Mutex::Autolock lock(mMutex);
|
||||
}
|
||||
|
||||
status_t JpegCompressor::start(Buffers *buffers, JpegListener *listener) {
|
||||
if (listener == NULL) {
|
||||
ALOGE("%s: NULL listener not allowed!", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Mutex::Autolock lock(mMutex);
|
||||
{
|
||||
Mutex::Autolock busyLock(mBusyMutex);
|
||||
|
||||
if (mIsBusy) {
|
||||
ALOGE("%s: Already processing a buffer!", __FUNCTION__);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
mIsBusy = true;
|
||||
mSynchronous = false;
|
||||
mBuffers = buffers;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
status_t res;
|
||||
res = run("EmulatedFakeCamera2::JpegCompressor");
|
||||
if (res != OK) {
|
||||
ALOGE("%s: Unable to start up compression thread: %s (%d)",
|
||||
__FUNCTION__, strerror(-res), res);
|
||||
delete mBuffers;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t JpegCompressor::compressSynchronous(Buffers *buffers) {
|
||||
status_t res;
|
||||
|
||||
Mutex::Autolock lock(mMutex);
|
||||
{
|
||||
Mutex::Autolock busyLock(mBusyMutex);
|
||||
|
||||
if (mIsBusy) {
|
||||
ALOGE("%s: Already processing a buffer!", __FUNCTION__);
|
||||
return INVALID_OPERATION;
|
||||
}
|
||||
|
||||
mIsBusy = true;
|
||||
mSynchronous = true;
|
||||
mBuffers = buffers;
|
||||
}
|
||||
|
||||
res = compress();
|
||||
|
||||
cleanUp();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t JpegCompressor::cancel() {
|
||||
requestExitAndWait();
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t JpegCompressor::readyToRun() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool JpegCompressor::threadLoop() {
|
||||
status_t res;
|
||||
ALOGV("%s: Starting compression thread", __FUNCTION__);
|
||||
|
||||
res = compress();
|
||||
|
||||
mListener->onJpegDone(mJpegBuffer, res == OK);
|
||||
|
||||
cleanUp();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t JpegCompressor::compress() {
|
||||
// Find source and target buffers. Assumes only one buffer matches
|
||||
// each condition!
|
||||
|
||||
bool foundJpeg = false, mFoundAux = false;
|
||||
for (size_t i = 0; i < mBuffers->size(); i++) {
|
||||
const StreamBuffer &b = (*mBuffers)[i];
|
||||
if (b.format == HAL_PIXEL_FORMAT_BLOB) {
|
||||
mJpegBuffer = b;
|
||||
mFoundJpeg = true;
|
||||
} else if (b.streamId <= 0) {
|
||||
mAuxBuffer = b;
|
||||
mFoundAux = true;
|
||||
}
|
||||
if (mFoundJpeg && mFoundAux) break;
|
||||
}
|
||||
if (!mFoundJpeg || !mFoundAux) {
|
||||
ALOGE("%s: Unable to find buffers for JPEG source/destination",
|
||||
__FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
// Set up error management
|
||||
|
||||
mJpegErrorInfo = NULL;
|
||||
JpegError error;
|
||||
error.parent = this;
|
||||
|
||||
mCInfo.err = jpeg_std_error(&error);
|
||||
mCInfo.err->error_exit = jpegErrorHandler;
|
||||
|
||||
jpeg_create_compress(&mCInfo);
|
||||
if (checkError("Error initializing compression")) return NO_INIT;
|
||||
|
||||
// Route compressed data straight to output stream buffer
|
||||
|
||||
JpegDestination jpegDestMgr;
|
||||
jpegDestMgr.parent = this;
|
||||
jpegDestMgr.init_destination = jpegInitDestination;
|
||||
jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer;
|
||||
jpegDestMgr.term_destination = jpegTermDestination;
|
||||
|
||||
mCInfo.dest = &jpegDestMgr;
|
||||
|
||||
// Set up compression parameters
|
||||
|
||||
mCInfo.image_width = mAuxBuffer.width;
|
||||
mCInfo.image_height = mAuxBuffer.height;
|
||||
mCInfo.input_components = 3;
|
||||
mCInfo.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults(&mCInfo);
|
||||
if (checkError("Error configuring defaults")) return NO_INIT;
|
||||
|
||||
// Do compression
|
||||
|
||||
jpeg_start_compress(&mCInfo, TRUE);
|
||||
if (checkError("Error starting compression")) return NO_INIT;
|
||||
|
||||
size_t rowStride = mAuxBuffer.stride * 3;
|
||||
const size_t kChunkSize = 32;
|
||||
while (mCInfo.next_scanline < mCInfo.image_height) {
|
||||
JSAMPROW chunk[kChunkSize];
|
||||
for (size_t i = 0 ; i < kChunkSize; i++) {
|
||||
chunk[i] = (JSAMPROW)
|
||||
(mAuxBuffer.img + (i + mCInfo.next_scanline) * rowStride);
|
||||
}
|
||||
jpeg_write_scanlines(&mCInfo, chunk, kChunkSize);
|
||||
if (checkError("Error while compressing")) return NO_INIT;
|
||||
if (exitPending()) {
|
||||
ALOGV("%s: Cancel called, exiting early", __FUNCTION__);
|
||||
return TIMED_OUT;
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_finish_compress(&mCInfo);
|
||||
if (checkError("Error while finishing compression")) return NO_INIT;
|
||||
|
||||
// All done
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool JpegCompressor::isBusy() {
|
||||
Mutex::Autolock busyLock(mBusyMutex);
|
||||
return mIsBusy;
|
||||
}
|
||||
|
||||
bool JpegCompressor::isStreamInUse(uint32_t id) {
|
||||
Mutex::Autolock lock(mBusyMutex);
|
||||
|
||||
if (mBuffers && mIsBusy) {
|
||||
for (size_t i = 0; i < mBuffers->size(); i++) {
|
||||
if ( (*mBuffers)[i].streamId == (int)id ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool JpegCompressor::waitForDone(nsecs_t timeout) {
|
||||
Mutex::Autolock lock(mBusyMutex);
|
||||
status_t res = OK;
|
||||
if (mIsBusy) {
|
||||
res = mDone.waitRelative(mBusyMutex, timeout);
|
||||
}
|
||||
return (res == OK);
|
||||
}
|
||||
|
||||
bool JpegCompressor::checkError(const char *msg) {
|
||||
if (mJpegErrorInfo) {
|
||||
char errBuffer[JMSG_LENGTH_MAX];
|
||||
mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer);
|
||||
ALOGE("%s: %s: %s",
|
||||
__FUNCTION__, msg, errBuffer);
|
||||
mJpegErrorInfo = NULL;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JpegCompressor::cleanUp() {
|
||||
status_t res;
|
||||
jpeg_destroy_compress(&mCInfo);
|
||||
Mutex::Autolock lock(mBusyMutex);
|
||||
|
||||
if (mFoundAux) {
|
||||
if (mAuxBuffer.streamId == 0) {
|
||||
delete[] mAuxBuffer.img;
|
||||
} else if (!mSynchronous) {
|
||||
mListener->onJpegInputDone(mAuxBuffer);
|
||||
}
|
||||
}
|
||||
if (!mSynchronous) {
|
||||
delete mBuffers;
|
||||
}
|
||||
|
||||
mBuffers = NULL;
|
||||
|
||||
mIsBusy = false;
|
||||
mDone.signal();
|
||||
}
|
||||
|
||||
void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) {
|
||||
JpegError *error = static_cast<JpegError*>(cinfo->err);
|
||||
error->parent->mJpegErrorInfo = cinfo;
|
||||
}
|
||||
|
||||
void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) {
|
||||
JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest);
|
||||
ALOGV("%s: Setting destination to %p, size %zu",
|
||||
__FUNCTION__, dest->parent->mJpegBuffer.img, kMaxJpegSize);
|
||||
dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer.img);
|
||||
dest->free_in_buffer = kMaxJpegSize;
|
||||
}
|
||||
|
||||
boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) {
|
||||
ALOGE("%s: JPEG destination buffer overflow!",
|
||||
__FUNCTION__);
|
||||
return true;
|
||||
}
|
||||
|
||||
void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) {
|
||||
ALOGV("%s: Done writing JPEG data. %zu bytes left in buffer",
|
||||
__FUNCTION__, cinfo->dest->free_in_buffer);
|
||||
}
|
||||
|
||||
JpegCompressor::JpegListener::~JpegListener() {
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
120
android/camera/fake-pipeline2/JpegCompressor.h
Normal file
120
android/camera/fake-pipeline2/JpegCompressor.h
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This class simulates a hardware JPEG compressor. It receives image buffers
|
||||
* in RGBA_8888 format, processes them in a worker thread, and then pushes them
|
||||
* out to their destination stream.
|
||||
*/
|
||||
|
||||
#ifndef HW_EMULATOR_CAMERA2_JPEG_H
|
||||
#define HW_EMULATOR_CAMERA2_JPEG_H
|
||||
|
||||
#include "utils/Thread.h"
|
||||
#include "utils/Mutex.h"
|
||||
#include "utils/Timers.h"
|
||||
|
||||
#include "Base.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
#include <jpeglib.h>
|
||||
}
|
||||
|
||||
namespace android {
|
||||
|
||||
class JpegCompressor: private Thread, public virtual RefBase {
|
||||
public:
|
||||
|
||||
JpegCompressor();
|
||||
~JpegCompressor();
|
||||
|
||||
struct JpegListener {
|
||||
// Called when JPEG compression has finished, or encountered an error
|
||||
virtual void onJpegDone(const StreamBuffer &jpegBuffer,
|
||||
bool success) = 0;
|
||||
// Called when the input buffer for JPEG is not needed any more,
|
||||
// if the buffer came from the framework.
|
||||
virtual void onJpegInputDone(const StreamBuffer &inputBuffer) = 0;
|
||||
virtual ~JpegListener();
|
||||
};
|
||||
|
||||
// Start compressing COMPRESSED format buffers; JpegCompressor takes
|
||||
// ownership of the Buffers vector.
|
||||
status_t start(Buffers *buffers, JpegListener *listener);
|
||||
|
||||
// Compress and block until buffer is complete.
|
||||
status_t compressSynchronous(Buffers *buffers);
|
||||
|
||||
status_t cancel();
|
||||
|
||||
bool isBusy();
|
||||
bool isStreamInUse(uint32_t id);
|
||||
|
||||
bool waitForDone(nsecs_t timeout);
|
||||
|
||||
// TODO: Measure this
|
||||
static const size_t kMaxJpegSize = 300000;
|
||||
|
||||
private:
|
||||
Mutex mBusyMutex;
|
||||
bool mIsBusy;
|
||||
Condition mDone;
|
||||
bool mSynchronous;
|
||||
|
||||
Mutex mMutex;
|
||||
|
||||
Buffers *mBuffers;
|
||||
JpegListener *mListener;
|
||||
|
||||
StreamBuffer mJpegBuffer, mAuxBuffer;
|
||||
bool mFoundJpeg, mFoundAux;
|
||||
|
||||
jpeg_compress_struct mCInfo;
|
||||
|
||||
struct JpegError : public jpeg_error_mgr {
|
||||
JpegCompressor *parent;
|
||||
};
|
||||
j_common_ptr mJpegErrorInfo;
|
||||
|
||||
struct JpegDestination : public jpeg_destination_mgr {
|
||||
JpegCompressor *parent;
|
||||
};
|
||||
|
||||
static void jpegErrorHandler(j_common_ptr cinfo);
|
||||
|
||||
static void jpegInitDestination(j_compress_ptr cinfo);
|
||||
static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo);
|
||||
static void jpegTermDestination(j_compress_ptr cinfo);
|
||||
|
||||
bool checkError(const char *msg);
|
||||
status_t compress();
|
||||
|
||||
void cleanUp();
|
||||
|
||||
/**
|
||||
* Inherited Thread virtual overrides
|
||||
*/
|
||||
private:
|
||||
virtual status_t readyToRun();
|
||||
virtual bool threadLoop();
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
478
android/camera/fake-pipeline2/Scene.cpp
Normal file
478
android/camera/fake-pipeline2/Scene.cpp
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "EmulatedCamera_Scene"
|
||||
#include <utils/Log.h>
|
||||
#include <stdlib.h>
|
||||
#include <cmath>
|
||||
#include "Scene.h"
|
||||
|
||||
// TODO: This should probably be done host-side in OpenGL for speed and better
|
||||
// quality
|
||||
|
||||
namespace android {
|
||||
|
||||
// Define single-letter shortcuts for scene definition, for directly indexing
|
||||
// mCurrentColors
|
||||
#define G (Scene::GRASS * Scene::NUM_CHANNELS)
|
||||
#define S (Scene::GRASS_SHADOW * Scene::NUM_CHANNELS)
|
||||
#define H (Scene::HILL * Scene::NUM_CHANNELS)
|
||||
#define W (Scene::WALL * Scene::NUM_CHANNELS)
|
||||
#define R (Scene::ROOF * Scene::NUM_CHANNELS)
|
||||
#define D (Scene::DOOR * Scene::NUM_CHANNELS)
|
||||
#define C (Scene::CHIMNEY * Scene::NUM_CHANNELS)
|
||||
#define I (Scene::WINDOW * Scene::NUM_CHANNELS)
|
||||
#define U (Scene::SUN * Scene::NUM_CHANNELS)
|
||||
#define K (Scene::SKY * Scene::NUM_CHANNELS)
|
||||
#define M (Scene::MOON * Scene::NUM_CHANNELS)
|
||||
|
||||
const int Scene::kSceneWidth = 20;
|
||||
const int Scene::kSceneHeight = 20;
|
||||
|
||||
const uint8_t Scene::kScene[Scene::kSceneWidth * Scene::kSceneHeight] = {
|
||||
// 5 10 15 20
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K, // 5
|
||||
K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,
|
||||
K,K,K,K,K,K,K,K,H,H,H,H,H,H,H,H,H,H,H,H,
|
||||
K,K,K,K,K,K,K,K,H,H,H,H,H,H,H,C,C,H,H,H,
|
||||
K,K,K,K,K,K,H,H,H,H,H,H,H,H,H,C,C,H,H,H,
|
||||
H,K,K,K,K,K,H,R,R,R,R,R,R,R,R,R,R,R,R,H, // 10
|
||||
H,K,K,K,K,H,H,R,R,R,R,R,R,R,R,R,R,R,R,H,
|
||||
H,H,H,K,K,H,H,R,R,R,R,R,R,R,R,R,R,R,R,H,
|
||||
H,H,H,K,K,H,H,H,W,W,W,W,W,W,W,W,W,W,H,H,
|
||||
S,S,S,G,G,S,S,S,W,W,W,W,W,W,W,W,W,W,S,S,
|
||||
S,G,G,G,G,S,S,S,W,I,I,W,D,D,W,I,I,W,S,S, // 15
|
||||
G,G,G,G,G,G,S,S,W,I,I,W,D,D,W,I,I,W,S,S,
|
||||
G,G,G,G,G,G,G,G,W,W,W,W,D,D,W,W,W,W,G,G,
|
||||
G,G,G,G,G,G,G,G,W,W,W,W,D,D,W,W,W,W,G,G,
|
||||
G,G,G,G,G,G,G,G,S,S,S,S,S,S,S,S,S,S,G,G,
|
||||
G,G,G,G,G,G,G,G,S,S,S,S,S,S,S,S,S,S,G,G, // 20
|
||||
// 5 10 15 20
|
||||
};
|
||||
|
||||
#undef G
|
||||
#undef S
|
||||
#undef H
|
||||
#undef W
|
||||
#undef R
|
||||
#undef D
|
||||
#undef C
|
||||
#undef I
|
||||
#undef U
|
||||
#undef K
|
||||
#undef M
|
||||
|
||||
Scene::Scene(
|
||||
int sensorWidthPx,
|
||||
int sensorHeightPx,
|
||||
float sensorSensitivity):
|
||||
mSensorWidth(sensorWidthPx),
|
||||
mSensorHeight(sensorHeightPx),
|
||||
mHour(12),
|
||||
mExposureDuration(0.033f),
|
||||
mSensorSensitivity(sensorSensitivity)
|
||||
{
|
||||
// Map scene to sensor pixels
|
||||
if (mSensorWidth > mSensorHeight) {
|
||||
mMapDiv = (mSensorWidth / (kSceneWidth + 1) ) + 1;
|
||||
} else {
|
||||
mMapDiv = (mSensorHeight / (kSceneHeight + 1) ) + 1;
|
||||
}
|
||||
mOffsetX = (kSceneWidth * mMapDiv - mSensorWidth) / 2;
|
||||
mOffsetY = (kSceneHeight * mMapDiv - mSensorHeight) / 2;
|
||||
|
||||
// Assume that sensor filters are sRGB primaries to start
|
||||
mFilterR[0] = 3.2406f; mFilterR[1] = -1.5372f; mFilterR[2] = -0.4986f;
|
||||
mFilterGr[0] = -0.9689f; mFilterGr[1] = 1.8758f; mFilterGr[2] = 0.0415f;
|
||||
mFilterGb[0] = -0.9689f; mFilterGb[1] = 1.8758f; mFilterGb[2] = 0.0415f;
|
||||
mFilterB[0] = 0.0557f; mFilterB[1] = -0.2040f; mFilterB[2] = 1.0570f;
|
||||
|
||||
|
||||
}
|
||||
|
||||
Scene::~Scene() {
|
||||
}
|
||||
|
||||
void Scene::setColorFilterXYZ(
|
||||
float rX, float rY, float rZ,
|
||||
float grX, float grY, float grZ,
|
||||
float gbX, float gbY, float gbZ,
|
||||
float bX, float bY, float bZ) {
|
||||
mFilterR[0] = rX; mFilterR[1] = rY; mFilterR[2] = rZ;
|
||||
mFilterGr[0] = grX; mFilterGr[1] = grY; mFilterGr[2] = grZ;
|
||||
mFilterGb[0] = gbX; mFilterGb[1] = gbY; mFilterGb[2] = gbZ;
|
||||
mFilterB[0] = bX; mFilterB[1] = bY; mFilterB[2] = bZ;
|
||||
}
|
||||
|
||||
void Scene::setHour(int hour) {
|
||||
ALOGV("Hour set to: %d", hour);
|
||||
mHour = hour % 24;
|
||||
}
|
||||
|
||||
int Scene::getHour() {
|
||||
return mHour;
|
||||
}
|
||||
|
||||
void Scene::setExposureDuration(float seconds) {
|
||||
mExposureDuration = seconds;
|
||||
}
|
||||
|
||||
void Scene::calculateScene(nsecs_t time) {
|
||||
// Calculate time fractions for interpolation
|
||||
int timeIdx = mHour / kTimeStep;
|
||||
int nextTimeIdx = (timeIdx + 1) % (24 / kTimeStep);
|
||||
const nsecs_t kOneHourInNsec = 1e9 * 60 * 60;
|
||||
nsecs_t timeSinceIdx = (mHour - timeIdx * kTimeStep) * kOneHourInNsec + time;
|
||||
float timeFrac = timeSinceIdx / (float)(kOneHourInNsec * kTimeStep);
|
||||
|
||||
// Determine overall sunlight levels
|
||||
float sunLux =
|
||||
kSunlight[timeIdx] * (1 - timeFrac) +
|
||||
kSunlight[nextTimeIdx] * timeFrac;
|
||||
ALOGV("Sun lux: %f", sunLux);
|
||||
|
||||
float sunShadeLux = sunLux * (kDaylightShadeIllum / kDirectSunIllum);
|
||||
|
||||
// Determine sun/shade illumination chromaticity
|
||||
float currentSunXY[2];
|
||||
float currentShadeXY[2];
|
||||
|
||||
const float *prevSunXY, *nextSunXY;
|
||||
const float *prevShadeXY, *nextShadeXY;
|
||||
if (kSunlight[timeIdx] == kSunsetIllum ||
|
||||
kSunlight[timeIdx] == kTwilightIllum) {
|
||||
prevSunXY = kSunsetXY;
|
||||
prevShadeXY = kSunsetXY;
|
||||
} else {
|
||||
prevSunXY = kDirectSunlightXY;
|
||||
prevShadeXY = kDaylightXY;
|
||||
}
|
||||
if (kSunlight[nextTimeIdx] == kSunsetIllum ||
|
||||
kSunlight[nextTimeIdx] == kTwilightIllum) {
|
||||
nextSunXY = kSunsetXY;
|
||||
nextShadeXY = kSunsetXY;
|
||||
} else {
|
||||
nextSunXY = kDirectSunlightXY;
|
||||
nextShadeXY = kDaylightXY;
|
||||
}
|
||||
currentSunXY[0] = prevSunXY[0] * (1 - timeFrac) +
|
||||
nextSunXY[0] * timeFrac;
|
||||
currentSunXY[1] = prevSunXY[1] * (1 - timeFrac) +
|
||||
nextSunXY[1] * timeFrac;
|
||||
|
||||
currentShadeXY[0] = prevShadeXY[0] * (1 - timeFrac) +
|
||||
nextShadeXY[0] * timeFrac;
|
||||
currentShadeXY[1] = prevShadeXY[1] * (1 - timeFrac) +
|
||||
nextShadeXY[1] * timeFrac;
|
||||
|
||||
ALOGV("Sun XY: %f, %f, Shade XY: %f, %f",
|
||||
currentSunXY[0], currentSunXY[1],
|
||||
currentShadeXY[0], currentShadeXY[1]);
|
||||
|
||||
// Converting for xyY to XYZ:
|
||||
// X = Y / y * x
|
||||
// Y = Y
|
||||
// Z = Y / y * (1 - x - y);
|
||||
float sunXYZ[3] = {
|
||||
sunLux / currentSunXY[1] * currentSunXY[0],
|
||||
sunLux,
|
||||
sunLux / currentSunXY[1] *
|
||||
(1 - currentSunXY[0] - currentSunXY[1])
|
||||
};
|
||||
float sunShadeXYZ[3] = {
|
||||
sunShadeLux / currentShadeXY[1] * currentShadeXY[0],
|
||||
sunShadeLux,
|
||||
sunShadeLux / currentShadeXY[1] *
|
||||
(1 - currentShadeXY[0] - currentShadeXY[1])
|
||||
};
|
||||
ALOGV("Sun XYZ: %f, %f, %f",
|
||||
sunXYZ[0], sunXYZ[1], sunXYZ[2]);
|
||||
ALOGV("Sun shade XYZ: %f, %f, %f",
|
||||
sunShadeXYZ[0], sunShadeXYZ[1], sunShadeXYZ[2]);
|
||||
|
||||
// Determine moonlight levels
|
||||
float moonLux =
|
||||
kMoonlight[timeIdx] * (1 - timeFrac) +
|
||||
kMoonlight[nextTimeIdx] * timeFrac;
|
||||
float moonShadeLux = moonLux * (kDaylightShadeIllum / kDirectSunIllum);
|
||||
|
||||
float moonXYZ[3] = {
|
||||
moonLux / kMoonlightXY[1] * kMoonlightXY[0],
|
||||
moonLux,
|
||||
moonLux / kMoonlightXY[1] *
|
||||
(1 - kMoonlightXY[0] - kMoonlightXY[1])
|
||||
};
|
||||
float moonShadeXYZ[3] = {
|
||||
moonShadeLux / kMoonlightXY[1] * kMoonlightXY[0],
|
||||
moonShadeLux,
|
||||
moonShadeLux / kMoonlightXY[1] *
|
||||
(1 - kMoonlightXY[0] - kMoonlightXY[1])
|
||||
};
|
||||
|
||||
// Determine starlight level
|
||||
const float kClearNightXYZ[3] = {
|
||||
kClearNightIllum / kMoonlightXY[1] * kMoonlightXY[0],
|
||||
kClearNightIllum,
|
||||
kClearNightIllum / kMoonlightXY[1] *
|
||||
(1 - kMoonlightXY[0] - kMoonlightXY[1])
|
||||
};
|
||||
|
||||
// Calculate direct and shaded light
|
||||
float directIllumXYZ[3] = {
|
||||
sunXYZ[0] + moonXYZ[0] + kClearNightXYZ[0],
|
||||
sunXYZ[1] + moonXYZ[1] + kClearNightXYZ[1],
|
||||
sunXYZ[2] + moonXYZ[2] + kClearNightXYZ[2],
|
||||
};
|
||||
|
||||
float shadeIllumXYZ[3] = {
|
||||
kClearNightXYZ[0],
|
||||
kClearNightXYZ[1],
|
||||
kClearNightXYZ[2]
|
||||
};
|
||||
|
||||
shadeIllumXYZ[0] += (mHour < kSunOverhead) ? sunXYZ[0] : sunShadeXYZ[0];
|
||||
shadeIllumXYZ[1] += (mHour < kSunOverhead) ? sunXYZ[1] : sunShadeXYZ[1];
|
||||
shadeIllumXYZ[2] += (mHour < kSunOverhead) ? sunXYZ[2] : sunShadeXYZ[2];
|
||||
|
||||
// Moon up period covers 23->0 transition, shift for simplicity
|
||||
int adjHour = (mHour + 12) % 24;
|
||||
int adjMoonOverhead = (kMoonOverhead + 12 ) % 24;
|
||||
shadeIllumXYZ[0] += (adjHour < adjMoonOverhead) ?
|
||||
moonXYZ[0] : moonShadeXYZ[0];
|
||||
shadeIllumXYZ[1] += (adjHour < adjMoonOverhead) ?
|
||||
moonXYZ[1] : moonShadeXYZ[1];
|
||||
shadeIllumXYZ[2] += (adjHour < adjMoonOverhead) ?
|
||||
moonXYZ[2] : moonShadeXYZ[2];
|
||||
|
||||
ALOGV("Direct XYZ: %f, %f, %f",
|
||||
directIllumXYZ[0],directIllumXYZ[1],directIllumXYZ[2]);
|
||||
ALOGV("Shade XYZ: %f, %f, %f",
|
||||
shadeIllumXYZ[0], shadeIllumXYZ[1], shadeIllumXYZ[2]);
|
||||
|
||||
for (int i = 0; i < NUM_MATERIALS; i++) {
|
||||
// Converting for xyY to XYZ:
|
||||
// X = Y / y * x
|
||||
// Y = Y
|
||||
// Z = Y / y * (1 - x - y);
|
||||
float matXYZ[3] = {
|
||||
kMaterials_xyY[i][2] / kMaterials_xyY[i][1] *
|
||||
kMaterials_xyY[i][0],
|
||||
kMaterials_xyY[i][2],
|
||||
kMaterials_xyY[i][2] / kMaterials_xyY[i][1] *
|
||||
(1 - kMaterials_xyY[i][0] - kMaterials_xyY[i][1])
|
||||
};
|
||||
|
||||
if (kMaterialsFlags[i] == 0 || kMaterialsFlags[i] & kSky) {
|
||||
matXYZ[0] *= directIllumXYZ[0];
|
||||
matXYZ[1] *= directIllumXYZ[1];
|
||||
matXYZ[2] *= directIllumXYZ[2];
|
||||
} else if (kMaterialsFlags[i] & kShadowed) {
|
||||
matXYZ[0] *= shadeIllumXYZ[0];
|
||||
matXYZ[1] *= shadeIllumXYZ[1];
|
||||
matXYZ[2] *= shadeIllumXYZ[2];
|
||||
} // else if (kMaterialsFlags[i] * kSelfLit), do nothing
|
||||
|
||||
ALOGV("Mat %d XYZ: %f, %f, %f", i, matXYZ[0], matXYZ[1], matXYZ[2]);
|
||||
float luxToElectrons = mSensorSensitivity * mExposureDuration /
|
||||
(kAperture * kAperture);
|
||||
mCurrentColors[i*NUM_CHANNELS + 0] =
|
||||
(mFilterR[0] * matXYZ[0] +
|
||||
mFilterR[1] * matXYZ[1] +
|
||||
mFilterR[2] * matXYZ[2])
|
||||
* luxToElectrons;
|
||||
mCurrentColors[i*NUM_CHANNELS + 1] =
|
||||
(mFilterGr[0] * matXYZ[0] +
|
||||
mFilterGr[1] * matXYZ[1] +
|
||||
mFilterGr[2] * matXYZ[2])
|
||||
* luxToElectrons;
|
||||
mCurrentColors[i*NUM_CHANNELS + 2] =
|
||||
(mFilterGb[0] * matXYZ[0] +
|
||||
mFilterGb[1] * matXYZ[1] +
|
||||
mFilterGb[2] * matXYZ[2])
|
||||
* luxToElectrons;
|
||||
mCurrentColors[i*NUM_CHANNELS + 3] =
|
||||
(mFilterB[0] * matXYZ[0] +
|
||||
mFilterB[1] * matXYZ[1] +
|
||||
mFilterB[2] * matXYZ[2])
|
||||
* luxToElectrons;
|
||||
|
||||
ALOGV("Color %d RGGB: %d, %d, %d, %d", i,
|
||||
mCurrentColors[i*NUM_CHANNELS + 0],
|
||||
mCurrentColors[i*NUM_CHANNELS + 1],
|
||||
mCurrentColors[i*NUM_CHANNELS + 2],
|
||||
mCurrentColors[i*NUM_CHANNELS + 3]);
|
||||
}
|
||||
// Shake viewpoint; horizontal and vertical sinusoids at roughly
|
||||
// human handshake frequencies
|
||||
mHandshakeX =
|
||||
( kFreq1Magnitude * std::sin(kHorizShakeFreq1 * timeSinceIdx) +
|
||||
kFreq2Magnitude * std::sin(kHorizShakeFreq2 * timeSinceIdx) ) *
|
||||
mMapDiv * kShakeFraction;
|
||||
|
||||
mHandshakeY =
|
||||
( kFreq1Magnitude * std::sin(kVertShakeFreq1 * timeSinceIdx) +
|
||||
kFreq2Magnitude * std::sin(kVertShakeFreq2 * timeSinceIdx) ) *
|
||||
mMapDiv * kShakeFraction;
|
||||
|
||||
// Set starting pixel
|
||||
setReadoutPixel(0,0);
|
||||
}
|
||||
|
||||
void Scene::setReadoutPixel(int x, int y) {
|
||||
mCurrentX = x;
|
||||
mCurrentY = y;
|
||||
mSubX = (x + mOffsetX + mHandshakeX) % mMapDiv;
|
||||
mSubY = (y + mOffsetY + mHandshakeY) % mMapDiv;
|
||||
mSceneX = (x + mOffsetX + mHandshakeX) / mMapDiv;
|
||||
mSceneY = (y + mOffsetY + mHandshakeY) / mMapDiv;
|
||||
mSceneIdx = mSceneY * kSceneWidth + mSceneX;
|
||||
mCurrentSceneMaterial = &(mCurrentColors[kScene[mSceneIdx]]);
|
||||
}
|
||||
|
||||
const uint32_t* Scene::getPixelElectrons() {
|
||||
const uint32_t *pixel = mCurrentSceneMaterial;
|
||||
mCurrentX++;
|
||||
mSubX++;
|
||||
if (mCurrentX >= mSensorWidth) {
|
||||
mCurrentX = 0;
|
||||
mCurrentY++;
|
||||
if (mCurrentY >= mSensorHeight) mCurrentY = 0;
|
||||
setReadoutPixel(mCurrentX, mCurrentY);
|
||||
} else if (mSubX > mMapDiv) {
|
||||
mSceneIdx++;
|
||||
mSceneX++;
|
||||
mCurrentSceneMaterial = &(mCurrentColors[kScene[mSceneIdx]]);
|
||||
mSubX = 0;
|
||||
}
|
||||
return pixel;
|
||||
}
|
||||
|
||||
// Handshake model constants.
|
||||
// Frequencies measured in a nanosecond timebase
|
||||
const float Scene::kHorizShakeFreq1 = 2 * M_PI * 2 / 1e9; // 2 Hz
|
||||
const float Scene::kHorizShakeFreq2 = 2 * M_PI * 13 / 1e9; // 13 Hz
|
||||
const float Scene::kVertShakeFreq1 = 2 * M_PI * 3 / 1e9; // 3 Hz
|
||||
const float Scene::kVertShakeFreq2 = 2 * M_PI * 11 / 1e9; // 1 Hz
|
||||
const float Scene::kFreq1Magnitude = 5;
|
||||
const float Scene::kFreq2Magnitude = 1;
|
||||
const float Scene::kShakeFraction = 0.03; // As a fraction of a scene tile
|
||||
|
||||
// RGB->YUV, Jpeg standard
|
||||
const float Scene::kRgb2Yuv[12] = {
|
||||
0.299f, 0.587f, 0.114f, 0.f,
|
||||
-0.16874f, -0.33126f, 0.5f, -128.f,
|
||||
0.5f, -0.41869f, -0.08131f, -128.f,
|
||||
};
|
||||
|
||||
// Aperture of imaging lens
|
||||
const float Scene::kAperture = 2.8;
|
||||
|
||||
// Sun illumination levels through the day
|
||||
const float Scene::kSunlight[24/kTimeStep] =
|
||||
{
|
||||
0, // 00:00
|
||||
0,
|
||||
0,
|
||||
kTwilightIllum, // 06:00
|
||||
kDirectSunIllum,
|
||||
kDirectSunIllum,
|
||||
kDirectSunIllum, // 12:00
|
||||
kDirectSunIllum,
|
||||
kDirectSunIllum,
|
||||
kSunsetIllum, // 18:00
|
||||
kTwilightIllum,
|
||||
0
|
||||
};
|
||||
|
||||
// Moon illumination levels through the day
|
||||
const float Scene::kMoonlight[24/kTimeStep] =
|
||||
{
|
||||
kFullMoonIllum, // 00:00
|
||||
kFullMoonIllum,
|
||||
0,
|
||||
0, // 06:00
|
||||
0,
|
||||
0,
|
||||
0, // 12:00
|
||||
0,
|
||||
0,
|
||||
0, // 18:00
|
||||
0,
|
||||
kFullMoonIllum
|
||||
};
|
||||
|
||||
const int Scene::kSunOverhead = 12;
|
||||
const int Scene::kMoonOverhead = 0;
|
||||
|
||||
// Used for sun illumination levels
|
||||
const float Scene::kDirectSunIllum = 100000;
|
||||
const float Scene::kSunsetIllum = 400;
|
||||
const float Scene::kTwilightIllum = 4;
|
||||
// Used for moon illumination levels
|
||||
const float Scene::kFullMoonIllum = 1;
|
||||
// Other illumination levels
|
||||
const float Scene::kDaylightShadeIllum = 20000;
|
||||
const float Scene::kClearNightIllum = 2e-3;
|
||||
const float Scene::kStarIllum = 2e-6;
|
||||
const float Scene::kLivingRoomIllum = 50;
|
||||
|
||||
const float Scene::kIncandescentXY[2] = { 0.44757f, 0.40745f};
|
||||
const float Scene::kDirectSunlightXY[2] = { 0.34842f, 0.35161f};
|
||||
const float Scene::kDaylightXY[2] = { 0.31271f, 0.32902f};
|
||||
const float Scene::kNoonSkyXY[2] = { 0.346f, 0.359f};
|
||||
const float Scene::kMoonlightXY[2] = { 0.34842f, 0.35161f};
|
||||
const float Scene::kSunsetXY[2] = { 0.527f, 0.413f};
|
||||
|
||||
const uint8_t Scene::kSelfLit = 0x01;
|
||||
const uint8_t Scene::kShadowed = 0x02;
|
||||
const uint8_t Scene::kSky = 0x04;
|
||||
|
||||
// For non-self-lit materials, the Y component is normalized with 1=full
|
||||
// reflectance; for self-lit materials, it's the constant illuminance in lux.
|
||||
const float Scene::kMaterials_xyY[Scene::NUM_MATERIALS][3] = {
|
||||
{ 0.3688f, 0.4501f, .1329f }, // GRASS
|
||||
{ 0.3688f, 0.4501f, .1329f }, // GRASS_SHADOW
|
||||
{ 0.3986f, 0.5002f, .4440f }, // HILL
|
||||
{ 0.3262f, 0.5040f, .2297f }, // WALL
|
||||
{ 0.4336f, 0.3787f, .1029f }, // ROOF
|
||||
{ 0.3316f, 0.2544f, .0639f }, // DOOR
|
||||
{ 0.3425f, 0.3577f, .0887f }, // CHIMNEY
|
||||
{ kIncandescentXY[0], kIncandescentXY[1], kLivingRoomIllum }, // WINDOW
|
||||
{ kDirectSunlightXY[0], kDirectSunlightXY[1], kDirectSunIllum }, // SUN
|
||||
{ kNoonSkyXY[0], kNoonSkyXY[1], kDaylightShadeIllum / kDirectSunIllum }, // SKY
|
||||
{ kMoonlightXY[0], kMoonlightXY[1], kFullMoonIllum } // MOON
|
||||
};
|
||||
|
||||
const uint8_t Scene::kMaterialsFlags[Scene::NUM_MATERIALS] = {
|
||||
0,
|
||||
kShadowed,
|
||||
kShadowed,
|
||||
kShadowed,
|
||||
kShadowed,
|
||||
kShadowed,
|
||||
kShadowed,
|
||||
kSelfLit,
|
||||
kSelfLit,
|
||||
kSky,
|
||||
kSelfLit,
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
191
android/camera/fake-pipeline2/Scene.h
Normal file
191
android/camera/fake-pipeline2/Scene.h
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Scene class implements a simple physical simulation of a scene, using the
|
||||
* CIE 1931 colorspace to represent light in physical units (lux).
|
||||
*
|
||||
* It's fairly approximate, but does provide a scene with realistic widely
|
||||
* variable illumination levels and colors over time.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_EMULATOR_CAMERA2_SCENE_H
|
||||
#define HW_EMULATOR_CAMERA2_SCENE_H
|
||||
|
||||
#include "utils/Timers.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
Scene(int sensorWidthPx,
|
||||
int sensorHeightPx,
|
||||
float sensorSensitivity);
|
||||
~Scene();
|
||||
|
||||
// Set the filter coefficients for the red, green, and blue filters on the
|
||||
// sensor. Used as an optimization to pre-calculate various illuminance
|
||||
// values. Two different green filters can be provided, to account for
|
||||
// possible cross-talk on a Bayer sensor. Must be called before
|
||||
// calculateScene.
|
||||
void setColorFilterXYZ(
|
||||
float rX, float rY, float rZ,
|
||||
float grX, float grY, float grZ,
|
||||
float gbX, float gbY, float gbZ,
|
||||
float bX, float bY, float bZ);
|
||||
|
||||
// Set time of day (24-hour clock). This controls the general light levels
|
||||
// in the scene. Must be called before calculateScene
|
||||
void setHour(int hour);
|
||||
// Get current hour
|
||||
int getHour();
|
||||
|
||||
// Set the duration of exposure for determining luminous exposure.
|
||||
// Must be called before calculateScene
|
||||
void setExposureDuration(float seconds);
|
||||
|
||||
// Calculate scene information for current hour and the time offset since
|
||||
// the hour. Must be called at least once before calling getLuminousExposure.
|
||||
// Resets pixel readout location to 0,0
|
||||
void calculateScene(nsecs_t time);
|
||||
|
||||
// Set sensor pixel readout location.
|
||||
void setReadoutPixel(int x, int y);
|
||||
|
||||
// Get sensor response in physical units (electrons) for light hitting the
|
||||
// current readout pixel, after passing through color filters. The readout
|
||||
// pixel will be auto-incremented. The returned array can be indexed with
|
||||
// ColorChannels.
|
||||
const uint32_t* getPixelElectrons();
|
||||
|
||||
enum ColorChannels {
|
||||
R = 0,
|
||||
Gr,
|
||||
Gb,
|
||||
B,
|
||||
Y,
|
||||
Cb,
|
||||
Cr,
|
||||
NUM_CHANNELS
|
||||
};
|
||||
|
||||
private:
|
||||
// Sensor color filtering coefficients in XYZ
|
||||
float mFilterR[3];
|
||||
float mFilterGr[3];
|
||||
float mFilterGb[3];
|
||||
float mFilterB[3];
|
||||
|
||||
int mOffsetX, mOffsetY;
|
||||
int mMapDiv;
|
||||
|
||||
int mHandshakeX, mHandshakeY;
|
||||
|
||||
int mSensorWidth;
|
||||
int mSensorHeight;
|
||||
int mCurrentX;
|
||||
int mCurrentY;
|
||||
int mSubX;
|
||||
int mSubY;
|
||||
int mSceneX;
|
||||
int mSceneY;
|
||||
int mSceneIdx;
|
||||
uint32_t *mCurrentSceneMaterial;
|
||||
|
||||
int mHour;
|
||||
float mExposureDuration;
|
||||
float mSensorSensitivity;
|
||||
|
||||
enum Materials {
|
||||
GRASS = 0,
|
||||
GRASS_SHADOW,
|
||||
HILL,
|
||||
WALL,
|
||||
ROOF,
|
||||
DOOR,
|
||||
CHIMNEY,
|
||||
WINDOW,
|
||||
SUN,
|
||||
SKY,
|
||||
MOON,
|
||||
NUM_MATERIALS
|
||||
};
|
||||
|
||||
uint32_t mCurrentColors[NUM_MATERIALS*NUM_CHANNELS];
|
||||
|
||||
/**
|
||||
* Constants for scene definition. These are various degrees of approximate.
|
||||
*/
|
||||
|
||||
// Fake handshake parameters. Two shake frequencies per axis, plus magnitude
|
||||
// as a fraction of a scene tile, and relative magnitudes for the frequencies
|
||||
static const float kHorizShakeFreq1;
|
||||
static const float kHorizShakeFreq2;
|
||||
static const float kVertShakeFreq1;
|
||||
static const float kVertShakeFreq2;
|
||||
static const float kFreq1Magnitude;
|
||||
static const float kFreq2Magnitude;
|
||||
|
||||
static const float kShakeFraction;
|
||||
|
||||
// RGB->YUV conversion
|
||||
static const float kRgb2Yuv[12];
|
||||
|
||||
// Aperture of imaging lens
|
||||
static const float kAperture;
|
||||
|
||||
// Sun, moon illuminance levels in 2-hour increments. These don't match any
|
||||
// real day anywhere.
|
||||
static const uint32_t kTimeStep = 2;
|
||||
static const float kSunlight[];
|
||||
static const float kMoonlight[];
|
||||
static const int kSunOverhead;
|
||||
static const int kMoonOverhead;
|
||||
|
||||
// Illumination levels for various conditions, in lux
|
||||
static const float kDirectSunIllum;
|
||||
static const float kDaylightShadeIllum;
|
||||
static const float kSunsetIllum;
|
||||
static const float kTwilightIllum;
|
||||
static const float kFullMoonIllum;
|
||||
static const float kClearNightIllum;
|
||||
static const float kStarIllum;
|
||||
static const float kLivingRoomIllum;
|
||||
|
||||
// Chromaticity of various illumination sources
|
||||
static const float kIncandescentXY[2];
|
||||
static const float kDirectSunlightXY[2];
|
||||
static const float kDaylightXY[2];
|
||||
static const float kNoonSkyXY[2];
|
||||
static const float kMoonlightXY[2];
|
||||
static const float kSunsetXY[2];
|
||||
|
||||
static const uint8_t kSelfLit;
|
||||
static const uint8_t kShadowed;
|
||||
static const uint8_t kSky;
|
||||
|
||||
static const float kMaterials_xyY[NUM_MATERIALS][3];
|
||||
static const uint8_t kMaterialsFlags[NUM_MATERIALS];
|
||||
|
||||
static const int kSceneWidth;
|
||||
static const int kSceneHeight;
|
||||
static const uint8_t kScene[];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HW_EMULATOR_CAMERA2_SCENE_H
|
||||
610
android/camera/fake-pipeline2/Sensor.cpp
Normal file
610
android/camera/fake-pipeline2/Sensor.cpp
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//#define LOG_NDEBUG 0
|
||||
//#define LOG_NNDEBUG 0
|
||||
#define LOG_TAG "EmulatedCamera2_Sensor"
|
||||
|
||||
#ifdef LOG_NNDEBUG
|
||||
#define ALOGVV(...) ALOGV(__VA_ARGS__)
|
||||
#else
|
||||
#define ALOGVV(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "../EmulatedFakeCamera2.h"
|
||||
#include "Sensor.h"
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include "system/camera_metadata.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
const unsigned int Sensor::kResolution[2] = {640, 480};
|
||||
const unsigned int Sensor::kActiveArray[4] = {0, 0, 640, 480};
|
||||
|
||||
//const nsecs_t Sensor::kExposureTimeRange[2] =
|
||||
// {1000L, 30000000000L} ; // 1 us - 30 sec
|
||||
//const nsecs_t Sensor::kFrameDurationRange[2] =
|
||||
// {33331760L, 30000000000L}; // ~1/30 s - 30 sec
|
||||
const nsecs_t Sensor::kExposureTimeRange[2] =
|
||||
{1000L, 300000000L} ; // 1 us - 0.3 sec
|
||||
const nsecs_t Sensor::kFrameDurationRange[2] =
|
||||
{33331760L, 300000000L}; // ~1/30 s - 0.3 sec
|
||||
|
||||
const nsecs_t Sensor::kMinVerticalBlank = 10000L;
|
||||
|
||||
const uint8_t Sensor::kColorFilterArrangement =
|
||||
ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB;
|
||||
|
||||
// Output image data characteristics
|
||||
const uint32_t Sensor::kMaxRawValue = 4000;
|
||||
const uint32_t Sensor::kBlackLevel = 1000;
|
||||
|
||||
// Sensor sensitivity
|
||||
const float Sensor::kSaturationVoltage = 0.520f;
|
||||
const uint32_t Sensor::kSaturationElectrons = 2000;
|
||||
const float Sensor::kVoltsPerLuxSecond = 0.100f;
|
||||
|
||||
const float Sensor::kElectronsPerLuxSecond =
|
||||
Sensor::kSaturationElectrons / Sensor::kSaturationVoltage
|
||||
* Sensor::kVoltsPerLuxSecond;
|
||||
|
||||
const float Sensor::kBaseGainFactor = (float)Sensor::kMaxRawValue /
|
||||
Sensor::kSaturationElectrons;
|
||||
|
||||
const float Sensor::kReadNoiseStddevBeforeGain = 1.177; // in electrons
|
||||
const float Sensor::kReadNoiseStddevAfterGain = 2.100; // in digital counts
|
||||
const float Sensor::kReadNoiseVarBeforeGain =
|
||||
Sensor::kReadNoiseStddevBeforeGain *
|
||||
Sensor::kReadNoiseStddevBeforeGain;
|
||||
const float Sensor::kReadNoiseVarAfterGain =
|
||||
Sensor::kReadNoiseStddevAfterGain *
|
||||
Sensor::kReadNoiseStddevAfterGain;
|
||||
|
||||
// While each row has to read out, reset, and then expose, the (reset +
|
||||
// expose) sequence can be overlapped by other row readouts, so the final
|
||||
// minimum frame duration is purely a function of row readout time, at least
|
||||
// if there's a reasonable number of rows.
|
||||
const nsecs_t Sensor::kRowReadoutTime =
|
||||
Sensor::kFrameDurationRange[0] / Sensor::kResolution[1];
|
||||
|
||||
const int32_t Sensor::kSensitivityRange[2] = {100, 1600};
|
||||
const uint32_t Sensor::kDefaultSensitivity = 100;
|
||||
|
||||
/** A few utility functions for math, normal distributions */
|
||||
|
||||
// Take advantage of IEEE floating-point format to calculate an approximate
|
||||
// square root. Accurate to within +-3.6%
|
||||
float sqrtf_approx(float r) {
|
||||
// Modifier is based on IEEE floating-point representation; the
|
||||
// manipulations boil down to finding approximate log2, dividing by two, and
|
||||
// then inverting the log2. A bias is added to make the relative error
|
||||
// symmetric about the real answer.
|
||||
const int32_t modifier = 0x1FBB4000;
|
||||
|
||||
int32_t r_i = *(int32_t*)(&r);
|
||||
r_i = (r_i >> 1) + modifier;
|
||||
|
||||
return *(float*)(&r_i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Sensor::Sensor():
|
||||
Thread(false),
|
||||
mGotVSync(false),
|
||||
mExposureTime(kFrameDurationRange[0]-kMinVerticalBlank),
|
||||
mFrameDuration(kFrameDurationRange[0]),
|
||||
mGainFactor(kDefaultSensitivity),
|
||||
mNextBuffers(NULL),
|
||||
mFrameNumber(0),
|
||||
mCapturedBuffers(NULL),
|
||||
mListener(NULL),
|
||||
mScene(kResolution[0], kResolution[1], kElectronsPerLuxSecond)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Sensor::~Sensor() {
|
||||
shutDown();
|
||||
}
|
||||
|
||||
status_t Sensor::startUp() {
|
||||
ALOGV("%s: E", __FUNCTION__);
|
||||
|
||||
int res;
|
||||
mCapturedBuffers = NULL;
|
||||
res = run("EmulatedFakeCamera2::Sensor",
|
||||
ANDROID_PRIORITY_URGENT_DISPLAY);
|
||||
|
||||
if (res != OK) {
|
||||
ALOGE("Unable to start up sensor capture thread: %d", res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t Sensor::shutDown() {
|
||||
ALOGV("%s: E", __FUNCTION__);
|
||||
|
||||
int res;
|
||||
res = requestExitAndWait();
|
||||
if (res != OK) {
|
||||
ALOGE("Unable to shut down sensor capture thread: %d", res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
Scene &Sensor::getScene() {
|
||||
return mScene;
|
||||
}
|
||||
|
||||
void Sensor::setExposureTime(uint64_t ns) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
ALOGVV("Exposure set to %f", ns/1000000.f);
|
||||
mExposureTime = ns;
|
||||
}
|
||||
|
||||
void Sensor::setFrameDuration(uint64_t ns) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
ALOGVV("Frame duration set to %f", ns/1000000.f);
|
||||
mFrameDuration = ns;
|
||||
}
|
||||
|
||||
void Sensor::setSensitivity(uint32_t gain) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
ALOGVV("Gain set to %d", gain);
|
||||
mGainFactor = gain;
|
||||
}
|
||||
|
||||
void Sensor::setDestinationBuffers(Buffers *buffers) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
mNextBuffers = buffers;
|
||||
}
|
||||
|
||||
void Sensor::setFrameNumber(uint32_t frameNumber) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
mFrameNumber = frameNumber;
|
||||
}
|
||||
|
||||
bool Sensor::waitForVSync(nsecs_t reltime) {
|
||||
int res;
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
|
||||
mGotVSync = false;
|
||||
res = mVSync.waitRelative(mControlMutex, reltime);
|
||||
if (res != OK && res != TIMED_OUT) {
|
||||
ALOGE("%s: Error waiting for VSync signal: %d", __FUNCTION__, res);
|
||||
return false;
|
||||
}
|
||||
return mGotVSync;
|
||||
}
|
||||
|
||||
bool Sensor::waitForNewFrame(nsecs_t reltime,
|
||||
nsecs_t *captureTime) {
|
||||
Mutex::Autolock lock(mReadoutMutex);
|
||||
uint8_t *ret;
|
||||
if (mCapturedBuffers == NULL) {
|
||||
int res;
|
||||
res = mReadoutAvailable.waitRelative(mReadoutMutex, reltime);
|
||||
if (res == TIMED_OUT) {
|
||||
return false;
|
||||
} else if (res != OK || mCapturedBuffers == NULL) {
|
||||
ALOGE("Error waiting for sensor readout signal: %d", res);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
mReadoutComplete.signal();
|
||||
}
|
||||
|
||||
*captureTime = mCaptureTime;
|
||||
mCapturedBuffers = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
Sensor::SensorListener::~SensorListener() {
|
||||
}
|
||||
|
||||
void Sensor::setSensorListener(SensorListener *listener) {
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
status_t Sensor::readyToRun() {
|
||||
ALOGV("Starting up sensor thread");
|
||||
mStartupTime = systemTime();
|
||||
mNextCaptureTime = 0;
|
||||
mNextCapturedBuffers = NULL;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool Sensor::threadLoop() {
|
||||
/**
|
||||
* Sensor capture operation main loop.
|
||||
*
|
||||
* Stages are out-of-order relative to a single frame's processing, but
|
||||
* in-order in time.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Stage 1: Read in latest control parameters
|
||||
*/
|
||||
uint64_t exposureDuration;
|
||||
uint64_t frameDuration;
|
||||
uint32_t gain;
|
||||
Buffers *nextBuffers;
|
||||
uint32_t frameNumber;
|
||||
SensorListener *listener = NULL;
|
||||
{
|
||||
Mutex::Autolock lock(mControlMutex);
|
||||
exposureDuration = mExposureTime;
|
||||
frameDuration = mFrameDuration;
|
||||
gain = mGainFactor;
|
||||
nextBuffers = mNextBuffers;
|
||||
frameNumber = mFrameNumber;
|
||||
listener = mListener;
|
||||
// Don't reuse a buffer set
|
||||
mNextBuffers = NULL;
|
||||
|
||||
// Signal VSync for start of readout
|
||||
ALOGVV("Sensor VSync");
|
||||
mGotVSync = true;
|
||||
mVSync.signal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage 3: Read out latest captured image
|
||||
*/
|
||||
|
||||
Buffers *capturedBuffers = NULL;
|
||||
nsecs_t captureTime = 0;
|
||||
|
||||
nsecs_t startRealTime = systemTime();
|
||||
// Stagefright cares about system time for timestamps, so base simulated
|
||||
// time on that.
|
||||
nsecs_t simulatedTime = startRealTime;
|
||||
nsecs_t frameEndRealTime = startRealTime + frameDuration;
|
||||
nsecs_t frameReadoutEndRealTime = startRealTime +
|
||||
kRowReadoutTime * kResolution[1];
|
||||
|
||||
if (mNextCapturedBuffers != NULL) {
|
||||
ALOGVV("Sensor starting readout");
|
||||
// Pretend we're doing readout now; will signal once enough time has elapsed
|
||||
capturedBuffers = mNextCapturedBuffers;
|
||||
captureTime = mNextCaptureTime;
|
||||
}
|
||||
simulatedTime += kRowReadoutTime + kMinVerticalBlank;
|
||||
|
||||
// TODO: Move this signal to another thread to simulate readout
|
||||
// time properly
|
||||
if (capturedBuffers != NULL) {
|
||||
ALOGVV("Sensor readout complete");
|
||||
Mutex::Autolock lock(mReadoutMutex);
|
||||
if (mCapturedBuffers != NULL) {
|
||||
ALOGV("Waiting for readout thread to catch up!");
|
||||
mReadoutComplete.wait(mReadoutMutex);
|
||||
}
|
||||
|
||||
mCapturedBuffers = capturedBuffers;
|
||||
mCaptureTime = captureTime;
|
||||
mReadoutAvailable.signal();
|
||||
capturedBuffers = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stage 2: Capture new image
|
||||
*/
|
||||
mNextCaptureTime = simulatedTime;
|
||||
mNextCapturedBuffers = nextBuffers;
|
||||
|
||||
if (mNextCapturedBuffers != NULL) {
|
||||
if (listener != NULL) {
|
||||
listener->onSensorEvent(frameNumber, SensorListener::EXPOSURE_START,
|
||||
mNextCaptureTime);
|
||||
}
|
||||
ALOGVV("Starting next capture: Exposure: %f ms, gain: %d",
|
||||
(float)exposureDuration/1e6, gain);
|
||||
mScene.setExposureDuration((float)exposureDuration/1e9);
|
||||
mScene.calculateScene(mNextCaptureTime);
|
||||
|
||||
// Might be adding more buffers, so size isn't constant
|
||||
for (size_t i = 0; i < mNextCapturedBuffers->size(); i++) {
|
||||
const StreamBuffer &b = (*mNextCapturedBuffers)[i];
|
||||
ALOGVV("Sensor capturing buffer %d: stream %d,"
|
||||
" %d x %d, format %x, stride %d, buf %p, img %p",
|
||||
i, b.streamId, b.width, b.height, b.format, b.stride,
|
||||
b.buffer, b.img);
|
||||
switch(b.format) {
|
||||
case HAL_PIXEL_FORMAT_RAW16:
|
||||
captureRaw(b.img, gain, b.stride);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGB_888:
|
||||
captureRGB(b.img, gain, b.stride);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_RGBA_8888:
|
||||
captureRGBA(b.img, gain, b.stride);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_BLOB:
|
||||
if (b.dataSpace != HAL_DATASPACE_DEPTH) {
|
||||
// Add auxillary buffer of the right size
|
||||
// Assumes only one BLOB (JPEG) buffer in
|
||||
// mNextCapturedBuffers
|
||||
StreamBuffer bAux;
|
||||
bAux.streamId = 0;
|
||||
bAux.width = b.width;
|
||||
bAux.height = b.height;
|
||||
bAux.format = HAL_PIXEL_FORMAT_RGB_888;
|
||||
bAux.stride = b.width;
|
||||
bAux.buffer = NULL;
|
||||
// TODO: Reuse these
|
||||
bAux.img = new uint8_t[b.width * b.height * 3];
|
||||
mNextCapturedBuffers->push_back(bAux);
|
||||
} else {
|
||||
captureDepthCloud(b.img);
|
||||
}
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
||||
captureNV21(b.img, gain, b.stride);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_YV12:
|
||||
// TODO:
|
||||
ALOGE("%s: Format %x is TODO", __FUNCTION__, b.format);
|
||||
break;
|
||||
case HAL_PIXEL_FORMAT_Y16:
|
||||
captureDepth(b.img, gain, b.stride);
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unknown format %x, no output", __FUNCTION__,
|
||||
b.format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALOGVV("Sensor vertical blanking interval");
|
||||
nsecs_t workDoneRealTime = systemTime();
|
||||
const nsecs_t timeAccuracy = 2e6; // 2 ms of imprecision is ok
|
||||
if (workDoneRealTime < frameEndRealTime - timeAccuracy) {
|
||||
timespec t;
|
||||
t.tv_sec = (frameEndRealTime - workDoneRealTime) / 1000000000L;
|
||||
t.tv_nsec = (frameEndRealTime - workDoneRealTime) % 1000000000L;
|
||||
|
||||
int ret;
|
||||
do {
|
||||
ret = nanosleep(&t, &t);
|
||||
} while (ret != 0);
|
||||
}
|
||||
nsecs_t endRealTime = systemTime();
|
||||
ALOGVV("Frame cycle took %d ms, target %d ms",
|
||||
(int)((endRealTime - startRealTime)/1000000),
|
||||
(int)(frameDuration / 1000000));
|
||||
return true;
|
||||
};
|
||||
|
||||
void Sensor::captureRaw(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||
float noiseVarGain = totalGain * totalGain;
|
||||
float readNoiseVar = kReadNoiseVarBeforeGain * noiseVarGain
|
||||
+ kReadNoiseVarAfterGain;
|
||||
|
||||
int bayerSelect[4] = {Scene::R, Scene::Gr, Scene::Gb, Scene::B}; // RGGB
|
||||
mScene.setReadoutPixel(0,0);
|
||||
for (unsigned int y = 0; y < kResolution[1]; y++ ) {
|
||||
int *bayerRow = bayerSelect + (y & 0x1) * 2;
|
||||
uint16_t *px = (uint16_t*)img + y * stride;
|
||||
for (unsigned int x = 0; x < kResolution[0]; x++) {
|
||||
uint32_t electronCount;
|
||||
electronCount = mScene.getPixelElectrons()[bayerRow[x & 0x1]];
|
||||
|
||||
// TODO: Better pixel saturation curve?
|
||||
electronCount = (electronCount < kSaturationElectrons) ?
|
||||
electronCount : kSaturationElectrons;
|
||||
|
||||
// TODO: Better A/D saturation curve?
|
||||
uint16_t rawCount = electronCount * totalGain;
|
||||
rawCount = (rawCount < kMaxRawValue) ? rawCount : kMaxRawValue;
|
||||
|
||||
// Calculate noise value
|
||||
// TODO: Use more-correct Gaussian instead of uniform noise
|
||||
float photonNoiseVar = electronCount * noiseVarGain;
|
||||
float noiseStddev = sqrtf_approx(readNoiseVar + photonNoiseVar);
|
||||
// Scaled to roughly match gaussian/uniform noise stddev
|
||||
float noiseSample = std::rand() * (2.5 / (1.0 + RAND_MAX)) - 1.25;
|
||||
|
||||
rawCount += kBlackLevel;
|
||||
rawCount += noiseStddev * noiseSample;
|
||||
|
||||
*px++ = rawCount;
|
||||
}
|
||||
// TODO: Handle this better
|
||||
//simulatedTime += kRowReadoutTime;
|
||||
}
|
||||
ALOGVV("Raw sensor image captured");
|
||||
}
|
||||
|
||||
void Sensor::captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||
uint32_t inc = kResolution[0] / stride;
|
||||
|
||||
for (unsigned int y = 0, outY = 0; y < kResolution[1]; y+=inc, outY++ ) {
|
||||
uint8_t *px = img + outY * stride * 4;
|
||||
mScene.setReadoutPixel(0, y);
|
||||
for (unsigned int x = 0; x < kResolution[0]; x+=inc) {
|
||||
uint32_t rCount, gCount, bCount;
|
||||
// TODO: Perfect demosaicing is a cheat
|
||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||
rCount = pixel[Scene::R] * scale64x;
|
||||
gCount = pixel[Scene::Gr] * scale64x;
|
||||
bCount = pixel[Scene::B] * scale64x;
|
||||
|
||||
*px++ = rCount < 255*64 ? rCount / 64 : 255;
|
||||
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
||||
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
||||
*px++ = 255;
|
||||
for (unsigned int j = 1; j < inc; j++)
|
||||
mScene.getPixelElectrons();
|
||||
}
|
||||
// TODO: Handle this better
|
||||
//simulatedTime += kRowReadoutTime;
|
||||
}
|
||||
ALOGVV("RGBA sensor image captured");
|
||||
}
|
||||
|
||||
void Sensor::captureRGB(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||
uint32_t inc = kResolution[0] / stride;
|
||||
|
||||
for (unsigned int y = 0, outY = 0; y < kResolution[1]; y += inc, outY++ ) {
|
||||
mScene.setReadoutPixel(0, y);
|
||||
uint8_t *px = img + outY * stride * 3;
|
||||
for (unsigned int x = 0; x < kResolution[0]; x += inc) {
|
||||
uint32_t rCount, gCount, bCount;
|
||||
// TODO: Perfect demosaicing is a cheat
|
||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||
rCount = pixel[Scene::R] * scale64x;
|
||||
gCount = pixel[Scene::Gr] * scale64x;
|
||||
bCount = pixel[Scene::B] * scale64x;
|
||||
|
||||
*px++ = rCount < 255*64 ? rCount / 64 : 255;
|
||||
*px++ = gCount < 255*64 ? gCount / 64 : 255;
|
||||
*px++ = bCount < 255*64 ? bCount / 64 : 255;
|
||||
for (unsigned int j = 1; j < inc; j++)
|
||||
mScene.getPixelElectrons();
|
||||
}
|
||||
// TODO: Handle this better
|
||||
//simulatedTime += kRowReadoutTime;
|
||||
}
|
||||
ALOGVV("RGB sensor image captured");
|
||||
}
|
||||
|
||||
void Sensor::captureNV21(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||
// Using fixed-point math with 6 bits of fractional precision.
|
||||
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
||||
const int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
||||
// In fixed-point math, saturation point of sensor after gain
|
||||
const int saturationPoint = 64 * 255;
|
||||
// Fixed-point coefficients for RGB-YUV transform
|
||||
// Based on JFIF RGB->YUV transform.
|
||||
// Cb/Cr offset scaled by 64x twice since they're applied post-multiply
|
||||
const int rgbToY[] = {19, 37, 7};
|
||||
const int rgbToCb[] = {-10,-21, 32, 524288};
|
||||
const int rgbToCr[] = {32,-26, -5, 524288};
|
||||
// Scale back to 8bpp non-fixed-point
|
||||
const int scaleOut = 64;
|
||||
const int scaleOutSq = scaleOut * scaleOut; // after multiplies
|
||||
|
||||
uint32_t inc = kResolution[0] / stride;
|
||||
uint32_t outH = kResolution[1] / inc;
|
||||
for (unsigned int y = 0, outY = 0;
|
||||
y < kResolution[1]; y+=inc, outY++) {
|
||||
uint8_t *pxY = img + outY * stride;
|
||||
uint8_t *pxVU = img + (outH + outY / 2) * stride;
|
||||
mScene.setReadoutPixel(0,y);
|
||||
for (unsigned int outX = 0; outX < stride; outX++) {
|
||||
int32_t rCount, gCount, bCount;
|
||||
// TODO: Perfect demosaicing is a cheat
|
||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||
rCount = pixel[Scene::R] * scale64x;
|
||||
rCount = rCount < saturationPoint ? rCount : saturationPoint;
|
||||
gCount = pixel[Scene::Gr] * scale64x;
|
||||
gCount = gCount < saturationPoint ? gCount : saturationPoint;
|
||||
bCount = pixel[Scene::B] * scale64x;
|
||||
bCount = bCount < saturationPoint ? bCount : saturationPoint;
|
||||
|
||||
*pxY++ = (rgbToY[0] * rCount +
|
||||
rgbToY[1] * gCount +
|
||||
rgbToY[2] * bCount) / scaleOutSq;
|
||||
if (outY % 2 == 0 && outX % 2 == 0) {
|
||||
*pxVU++ = (rgbToCr[0] * rCount +
|
||||
rgbToCr[1] * gCount +
|
||||
rgbToCr[2] * bCount +
|
||||
rgbToCr[3]) / scaleOutSq;
|
||||
*pxVU++ = (rgbToCb[0] * rCount +
|
||||
rgbToCb[1] * gCount +
|
||||
rgbToCb[2] * bCount +
|
||||
rgbToCb[3]) / scaleOutSq;
|
||||
}
|
||||
for (unsigned int j = 1; j < inc; j++)
|
||||
mScene.getPixelElectrons();
|
||||
}
|
||||
}
|
||||
ALOGVV("NV21 sensor image captured");
|
||||
}
|
||||
|
||||
void Sensor::captureDepth(uint8_t *img, uint32_t gain, uint32_t stride) {
|
||||
float totalGain = gain/100.0 * kBaseGainFactor;
|
||||
// In fixed-point math, calculate scaling factor to 13bpp millimeters
|
||||
int scale64x = 64 * totalGain * 8191 / kMaxRawValue;
|
||||
uint32_t inc = kResolution[0] / stride;
|
||||
|
||||
for (unsigned int y = 0, outY = 0; y < kResolution[1]; y += inc, outY++ ) {
|
||||
mScene.setReadoutPixel(0, y);
|
||||
uint16_t *px = ((uint16_t*)img) + outY * stride;
|
||||
for (unsigned int x = 0; x < kResolution[0]; x += inc) {
|
||||
uint32_t depthCount;
|
||||
// TODO: Make up real depth scene instead of using green channel
|
||||
// as depth
|
||||
const uint32_t *pixel = mScene.getPixelElectrons();
|
||||
depthCount = pixel[Scene::Gr] * scale64x;
|
||||
|
||||
*px++ = depthCount < 8191*64 ? depthCount / 64 : 0;
|
||||
for (unsigned int j = 1; j < inc; j++)
|
||||
mScene.getPixelElectrons();
|
||||
}
|
||||
// TODO: Handle this better
|
||||
//simulatedTime += kRowReadoutTime;
|
||||
}
|
||||
ALOGVV("Depth sensor image captured");
|
||||
}
|
||||
|
||||
void Sensor::captureDepthCloud(uint8_t *img) {
|
||||
|
||||
android_depth_points *cloud = reinterpret_cast<android_depth_points*>(img);
|
||||
|
||||
cloud->num_points = 16;
|
||||
|
||||
// TODO: Create point cloud values that match RGB scene
|
||||
const int FLOATS_PER_POINT = 4;
|
||||
const float JITTER_STDDEV = 0.1f;
|
||||
for (size_t y = 0, i = 0; y < 4; y++) {
|
||||
for (size_t x = 0; x < 4; x++, i++) {
|
||||
float randSampleX = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
|
||||
randSampleX *= JITTER_STDDEV;
|
||||
|
||||
float randSampleY = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
|
||||
randSampleY *= JITTER_STDDEV;
|
||||
|
||||
float randSampleZ = std::rand() * (2.5f / (1.0f + RAND_MAX)) - 1.25f;
|
||||
randSampleZ *= JITTER_STDDEV;
|
||||
|
||||
cloud->xyzc_points[i * FLOATS_PER_POINT + 0] = x - 1.5f + randSampleX;
|
||||
cloud->xyzc_points[i * FLOATS_PER_POINT + 1] = y - 1.5f + randSampleY;
|
||||
cloud->xyzc_points[i * FLOATS_PER_POINT + 2] = 3.f + randSampleZ;
|
||||
cloud->xyzc_points[i * FLOATS_PER_POINT + 3] = 0.8f;
|
||||
}
|
||||
}
|
||||
|
||||
ALOGVV("Depth point cloud captured");
|
||||
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
245
android/camera/fake-pipeline2/Sensor.h
Normal file
245
android/camera/fake-pipeline2/Sensor.h
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is a simple simulation of a typical CMOS cellphone imager chip,
|
||||
* which outputs 12-bit Bayer-mosaic raw images.
|
||||
*
|
||||
* Unlike most real image sensors, this one's native color space is linear sRGB.
|
||||
*
|
||||
* The sensor is abstracted as operating as a pipeline 3 stages deep;
|
||||
* conceptually, each frame to be captured goes through these three stages. The
|
||||
* processing step for the sensor is marked off by vertical sync signals, which
|
||||
* indicate the start of readout of the oldest frame. The interval between
|
||||
* processing steps depends on the frame duration of the frame currently being
|
||||
* captured. The stages are 1) configure, 2) capture, and 3) readout. During
|
||||
* configuration, the sensor's registers for settings such as exposure time,
|
||||
* frame duration, and gain are set for the next frame to be captured. In stage
|
||||
* 2, the image data for the frame is actually captured by the sensor. Finally,
|
||||
* in stage 3, the just-captured data is read out and sent to the rest of the
|
||||
* system.
|
||||
*
|
||||
* The sensor is assumed to be rolling-shutter, so low-numbered rows of the
|
||||
* sensor are exposed earlier in time than larger-numbered rows, with the time
|
||||
* offset between each row being equal to the row readout time.
|
||||
*
|
||||
* The characteristics of this sensor don't correspond to any actual sensor,
|
||||
* but are not far off typical sensors.
|
||||
*
|
||||
* Example timing diagram, with three frames:
|
||||
* Frame 0-1: Frame duration 50 ms, exposure time 20 ms.
|
||||
* Frame 2: Frame duration 75 ms, exposure time 65 ms.
|
||||
* Legend:
|
||||
* C = update sensor registers for frame
|
||||
* v = row in reset (vertical blanking interval)
|
||||
* E = row capturing image data
|
||||
* R = row being read out
|
||||
* | = vertical sync signal
|
||||
*time(ms)| 0 55 105 155 230 270
|
||||
* Frame 0| :configure : capture : readout : : :
|
||||
* Row # | ..|CCCC______|_________|_________| : :
|
||||
* 0 | :\ \vvvvvEEEER \ : :
|
||||
* 500 | : \ \vvvvvEEEER \ : :
|
||||
* 1000 | : \ \vvvvvEEEER \ : :
|
||||
* 1500 | : \ \vvvvvEEEER \ : :
|
||||
* 2000 | : \__________\vvvvvEEEER_________\ : :
|
||||
* Frame 1| : configure capture readout : :
|
||||
* Row # | : |CCCC_____|_________|______________| :
|
||||
* 0 | : :\ \vvvvvEEEER \ :
|
||||
* 500 | : : \ \vvvvvEEEER \ :
|
||||
* 1000 | : : \ \vvvvvEEEER \ :
|
||||
* 1500 | : : \ \vvvvvEEEER \ :
|
||||
* 2000 | : : \_________\vvvvvEEEER______________\ :
|
||||
* Frame 2| : : configure capture readout:
|
||||
* Row # | : : |CCCC_____|______________|_______|...
|
||||
* 0 | : : :\ \vEEEEEEEEEEEEER \
|
||||
* 500 | : : : \ \vEEEEEEEEEEEEER \
|
||||
* 1000 | : : : \ \vEEEEEEEEEEEEER \
|
||||
* 1500 | : : : \ \vEEEEEEEEEEEEER \
|
||||
* 2000 | : : : \_________\vEEEEEEEEEEEEER_______\
|
||||
*/
|
||||
|
||||
#ifndef HW_EMULATOR_CAMERA2_SENSOR_H
|
||||
#define HW_EMULATOR_CAMERA2_SENSOR_H
|
||||
|
||||
#include "utils/Thread.h"
|
||||
#include "utils/Mutex.h"
|
||||
#include "utils/Timers.h"
|
||||
|
||||
#include "Scene.h"
|
||||
#include "Base.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
class EmulatedFakeCamera2;
|
||||
|
||||
class Sensor: private Thread, public virtual RefBase {
|
||||
public:
|
||||
|
||||
Sensor();
|
||||
~Sensor();
|
||||
|
||||
/*
|
||||
* Power control
|
||||
*/
|
||||
|
||||
status_t startUp();
|
||||
status_t shutDown();
|
||||
|
||||
/*
|
||||
* Access to scene
|
||||
*/
|
||||
Scene &getScene();
|
||||
|
||||
/*
|
||||
* Controls that can be updated every frame
|
||||
*/
|
||||
|
||||
void setExposureTime(uint64_t ns);
|
||||
void setFrameDuration(uint64_t ns);
|
||||
void setSensitivity(uint32_t gain);
|
||||
// Buffer must be at least stride*height*2 bytes in size
|
||||
void setDestinationBuffers(Buffers *buffers);
|
||||
// To simplify tracking sensor's current frame
|
||||
void setFrameNumber(uint32_t frameNumber);
|
||||
|
||||
/*
|
||||
* Controls that cause reconfiguration delay
|
||||
*/
|
||||
|
||||
void setBinning(int horizontalFactor, int verticalFactor);
|
||||
|
||||
/*
|
||||
* Synchronizing with sensor operation (vertical sync)
|
||||
*/
|
||||
|
||||
// Wait until the sensor outputs its next vertical sync signal, meaning it
|
||||
// is starting readout of its latest frame of data. Returns true if vertical
|
||||
// sync is signaled, false if the wait timed out.
|
||||
bool waitForVSync(nsecs_t reltime);
|
||||
|
||||
// Wait until a new frame has been read out, and then return the time
|
||||
// capture started. May return immediately if a new frame has been pushed
|
||||
// since the last wait for a new frame. Returns true if new frame is
|
||||
// returned, false if timed out.
|
||||
bool waitForNewFrame(nsecs_t reltime,
|
||||
nsecs_t *captureTime);
|
||||
|
||||
/*
|
||||
* Interrupt event servicing from the sensor. Only triggers for sensor
|
||||
* cycles that have valid buffers to write to.
|
||||
*/
|
||||
struct SensorListener {
|
||||
enum Event {
|
||||
EXPOSURE_START, // Start of exposure
|
||||
};
|
||||
|
||||
virtual void onSensorEvent(uint32_t frameNumber, Event e,
|
||||
nsecs_t timestamp) = 0;
|
||||
virtual ~SensorListener();
|
||||
};
|
||||
|
||||
void setSensorListener(SensorListener *listener);
|
||||
|
||||
/**
|
||||
* Static sensor characteristics
|
||||
*/
|
||||
static const unsigned int kResolution[2];
|
||||
static const unsigned int kActiveArray[4];
|
||||
|
||||
static const nsecs_t kExposureTimeRange[2];
|
||||
static const nsecs_t kFrameDurationRange[2];
|
||||
static const nsecs_t kMinVerticalBlank;
|
||||
|
||||
static const uint8_t kColorFilterArrangement;
|
||||
|
||||
// Output image data characteristics
|
||||
static const uint32_t kMaxRawValue;
|
||||
static const uint32_t kBlackLevel;
|
||||
// Sensor sensitivity, approximate
|
||||
|
||||
static const float kSaturationVoltage;
|
||||
static const uint32_t kSaturationElectrons;
|
||||
static const float kVoltsPerLuxSecond;
|
||||
static const float kElectronsPerLuxSecond;
|
||||
|
||||
static const float kBaseGainFactor;
|
||||
|
||||
static const float kReadNoiseStddevBeforeGain; // In electrons
|
||||
static const float kReadNoiseStddevAfterGain; // In raw digital units
|
||||
static const float kReadNoiseVarBeforeGain;
|
||||
static const float kReadNoiseVarAfterGain;
|
||||
|
||||
// While each row has to read out, reset, and then expose, the (reset +
|
||||
// expose) sequence can be overlapped by other row readouts, so the final
|
||||
// minimum frame duration is purely a function of row readout time, at least
|
||||
// if there's a reasonable number of rows.
|
||||
static const nsecs_t kRowReadoutTime;
|
||||
|
||||
static const int32_t kSensitivityRange[2];
|
||||
static const uint32_t kDefaultSensitivity;
|
||||
|
||||
private:
|
||||
Mutex mControlMutex; // Lock before accessing control parameters
|
||||
// Start of control parameters
|
||||
Condition mVSync;
|
||||
bool mGotVSync;
|
||||
uint64_t mExposureTime;
|
||||
uint64_t mFrameDuration;
|
||||
uint32_t mGainFactor;
|
||||
Buffers *mNextBuffers;
|
||||
uint32_t mFrameNumber;
|
||||
|
||||
// End of control parameters
|
||||
|
||||
Mutex mReadoutMutex; // Lock before accessing readout variables
|
||||
// Start of readout variables
|
||||
Condition mReadoutAvailable;
|
||||
Condition mReadoutComplete;
|
||||
Buffers *mCapturedBuffers;
|
||||
nsecs_t mCaptureTime;
|
||||
SensorListener *mListener;
|
||||
// End of readout variables
|
||||
|
||||
// Time of sensor startup, used for simulation zero-time point
|
||||
nsecs_t mStartupTime;
|
||||
|
||||
/**
|
||||
* Inherited Thread virtual overrides, and members only used by the
|
||||
* processing thread
|
||||
*/
|
||||
private:
|
||||
virtual status_t readyToRun();
|
||||
|
||||
virtual bool threadLoop();
|
||||
|
||||
nsecs_t mNextCaptureTime;
|
||||
Buffers *mNextCapturedBuffers;
|
||||
|
||||
Scene mScene;
|
||||
|
||||
void captureRaw(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||
void captureRGBA(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||
void captureRGB(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||
void captureNV21(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||
void captureDepth(uint8_t *img, uint32_t gain, uint32_t stride);
|
||||
void captureDepthCloud(uint8_t *img);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HW_EMULATOR_CAMERA2_SENSOR_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue