/*
* 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 "Scene.h"
#include <stdlib.h>
#include <utils/Log.h>
#include <cmath>
// 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