| /* |
| * 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 "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 |
| mHandshakeX = rand() % mMapDiv/4 - mMapDiv/8; |
| mHandshakeY = rand() % mMapDiv/4 - mMapDiv/8; |
| // 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; |
| } |
| |
| // 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 |