You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
12 KiB
325 lines
12 KiB
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "fake-pipeline2/Base.h"
|
|
#include "fake-pipeline2/Scene.h"
|
|
#include "QemuClient.h"
|
|
#include <gralloc_cb_bp.h>
|
|
|
|
#include <ui/GraphicBufferAllocator.h>
|
|
#include <ui/GraphicBufferMapper.h>
|
|
#include <ui/Rect.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
#include <utils/Timers.h>
|
|
|
|
using namespace android;
|
|
|
|
|
|
const nsecs_t kExposureTimeRange[2] =
|
|
{1000L, 300000000L} ; // 1 us - 0.3 sec
|
|
const nsecs_t kFrameDurationRange[2] =
|
|
{33331760L, 300000000L}; // ~1/30 s - 0.3 sec
|
|
|
|
const nsecs_t kMinVerticalBlank = 10000L;
|
|
|
|
const uint8_t kColorFilterArrangement =
|
|
ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB;
|
|
|
|
// Output image data characteristics
|
|
const uint32_t kMaxRawValue = 4000;
|
|
const uint32_t kBlackLevel = 1000;
|
|
|
|
// Sensor sensitivity
|
|
const float kSaturationVoltage = 0.520f;
|
|
const uint32_t kSaturationElectrons = 2000;
|
|
const float kVoltsPerLuxSecond = 0.100f;
|
|
|
|
const float kElectronsPerLuxSecond =
|
|
kSaturationElectrons / kSaturationVoltage
|
|
* kVoltsPerLuxSecond;
|
|
|
|
const float kBaseGainFactor = (float)kMaxRawValue /
|
|
kSaturationElectrons;
|
|
|
|
const float kReadNoiseStddevBeforeGain = 1.177; // in electrons
|
|
const float kReadNoiseStddevAfterGain = 2.100; // in digital counts
|
|
const float kReadNoiseVarBeforeGain =
|
|
kReadNoiseStddevBeforeGain *
|
|
kReadNoiseStddevBeforeGain;
|
|
const float kReadNoiseVarAfterGain =
|
|
kReadNoiseStddevAfterGain *
|
|
kReadNoiseStddevAfterGain;
|
|
|
|
const int32_t kSensitivityRange[2] = {100, 1600};
|
|
const uint32_t kDefaultSensitivity = 100;
|
|
|
|
void captureRGBA(uint8_t *img, uint32_t gain, uint32_t width, uint32_t height, Scene& scene, uint32_t sWidth, uint32_t sHeight) {
|
|
float totalGain = gain/100.0 * kBaseGainFactor;
|
|
// In fixed-point math, calculate total scaling from electrons to 8bpp
|
|
int scale64x = 64 * totalGain * 255 / kMaxRawValue;
|
|
unsigned int DivH= (float)sHeight/height * (0x1 << 10);
|
|
unsigned int DivW = (float)sWidth/width * (0x1 << 10);
|
|
|
|
for (unsigned int outY = 0; outY < height; outY++) {
|
|
unsigned int y = outY * DivH >> 10;
|
|
uint8_t *px = img + outY * width * 4;
|
|
scene.setReadoutPixel(0, y);
|
|
unsigned int lastX = 0;
|
|
const uint32_t *pixel = scene.getPixelElectrons();
|
|
for (unsigned int outX = 0; outX < width; outX++) {
|
|
uint32_t rCount, gCount, bCount;
|
|
unsigned int x = outX * DivW >> 10;
|
|
if (x - lastX > 0) {
|
|
for (unsigned int k = 0; k < (x-lastX); k++) {
|
|
pixel = scene.getPixelElectrons();
|
|
}
|
|
}
|
|
lastX = x;
|
|
// TODO: Perfect demosaicing is a cheat
|
|
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;
|
|
}
|
|
// TODO: Handle this better
|
|
//simulatedTime += mRowReadoutTime;
|
|
}
|
|
}
|
|
|
|
void captureYU12(uint8_t *img, uint32_t gain, uint32_t width, uint32_t height, Scene& scene, uint32_t sWidth, uint32_t sHeight) {
|
|
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
|
|
float rgbToY[] = {19.0, 37.0, 7.0, 0.0};
|
|
float rgbToCb[] = {-10.0,-21.0, 32.0, 524288.0};
|
|
float rgbToCr[] = {32.0,-26.0, -5.0, 524288.0};
|
|
// Scale back to 8bpp non-fixed-point
|
|
const int scaleOut = 64;
|
|
const int scaleOutSq = scaleOut * scaleOut; // after multiplies
|
|
const double invscaleOutSq = 1.0/scaleOutSq;
|
|
for (int i=0; i < 4; ++i) {
|
|
rgbToY[i] *= invscaleOutSq;
|
|
rgbToCb[i] *= invscaleOutSq;
|
|
rgbToCr[i] *= invscaleOutSq;
|
|
}
|
|
|
|
unsigned int DivH= (float)sHeight/height * (0x1 << 10);
|
|
unsigned int DivW = (float)sWidth/width * (0x1 << 10);
|
|
for (unsigned int outY = 0; outY < height; outY++) {
|
|
unsigned int y = outY * DivH >> 10;
|
|
uint8_t *pxY = img + outY * width;
|
|
uint8_t *pxVU = img + (height + outY / 2) * width;
|
|
uint8_t *pxU = img + height * width + (outY / 2) * (width / 2);
|
|
uint8_t *pxV = pxU + (height / 2) * (width / 2);
|
|
scene.setReadoutPixel(0, y);
|
|
unsigned int lastX = 0;
|
|
const uint32_t *pixel = scene.getPixelElectrons();
|
|
for (unsigned int outX = 0; outX < width; outX++) {
|
|
int32_t rCount, gCount, bCount;
|
|
unsigned int x = outX * DivW >> 10;
|
|
if (x - lastX > 0) {
|
|
for (unsigned int k = 0; k < (x-lastX); k++) {
|
|
pixel = scene.getPixelElectrons();
|
|
}
|
|
}
|
|
lastX = x;
|
|
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);
|
|
if (outY % 2 == 0 && outX % 2 == 0) {
|
|
*pxV++ = (rgbToCr[0] * rCount + rgbToCr[1] * gCount + rgbToCr[2] * bCount + rgbToCr[3]);
|
|
*pxU++ = (rgbToCb[0] * rCount + rgbToCb[1] * gCount + rgbToCb[2] * bCount + rgbToCb[3]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test the capture speed of qemu camera, e.g., webcam and virtual scene
|
|
int main(int argc, char* argv[]) {
|
|
using ::android::GraphicBufferAllocator;
|
|
using ::android::GraphicBufferMapper;
|
|
|
|
|
|
uint32_t pixFmt;
|
|
int uiFmt;
|
|
bool v1 = false;
|
|
bool fake = false;
|
|
std::vector<nsecs_t> report;
|
|
uint32_t sceneWidth;
|
|
uint32_t sceneHeight;
|
|
|
|
if (!strncmp(argv[1], "RGB", 3)) {
|
|
pixFmt = V4L2_PIX_FMT_RGB32;
|
|
uiFmt = HAL_PIXEL_FORMAT_RGBA_8888;
|
|
} else if (!strncmp(argv[1], "NV21", 3)) {
|
|
pixFmt = V4L2_PIX_FMT_NV21;
|
|
uiFmt = HAL_PIXEL_FORMAT_YCbCr_420_888;
|
|
} else if (!strncmp(argv[1], "YV12", 3)) {
|
|
pixFmt = V4L2_PIX_FMT_YVU420;
|
|
uiFmt = HAL_PIXEL_FORMAT_YCbCr_420_888;
|
|
} else if (!strncmp(argv[1], "YU12", 3)) {
|
|
pixFmt = V4L2_PIX_FMT_YUV420;
|
|
uiFmt = HAL_PIXEL_FORMAT_YCbCr_420_888;
|
|
} else {
|
|
printf("format error, use RGB, NV21, YV12 or YU12");
|
|
return -1;
|
|
}
|
|
uint32_t width = atoi(argv[2]);
|
|
uint32_t height = atoi(argv[3]);
|
|
uint32_t repeated = atoi(argv[4]);
|
|
std::string deviceName;
|
|
if (!strncmp(argv[5], "web", 3)) {
|
|
deviceName = "name=/dev/video0";
|
|
} else if (!strncmp(argv[5], "vir", 3)) {
|
|
deviceName = "name=virtualscene";
|
|
} else if (!strncmp(argv[5], "fak", 3)){
|
|
fake = true;
|
|
sceneWidth = atoi(argv[6]);
|
|
sceneHeight = atoi(argv[7]);
|
|
} else {
|
|
printf("device error, use web or virtual");
|
|
return -1;
|
|
}
|
|
|
|
if (fake) {
|
|
std::vector<uint8_t> buf(width * height * 4);
|
|
Scene scene(width, height, kElectronsPerLuxSecond);
|
|
for (int i = 0 ; i < repeated; i++) {
|
|
nsecs_t start = systemTime();
|
|
if (pixFmt == V4L2_PIX_FMT_RGB32) {
|
|
captureRGBA(buf.data(), 0, width, height, scene, sceneWidth, sceneHeight);
|
|
} else {
|
|
captureYU12(buf.data(), 0, width, height, scene, sceneWidth, sceneHeight);
|
|
}
|
|
nsecs_t end = systemTime();
|
|
report.push_back(end - start);
|
|
}
|
|
}
|
|
else {
|
|
if (argc > 6 && !strncmp(argv[6], "v1", 2)) {
|
|
v1 = true;
|
|
}
|
|
// Open qemu pipe
|
|
CameraQemuClient client;
|
|
int ret = client.connectClient(deviceName.c_str());
|
|
if (ret != NO_ERROR) {
|
|
printf("Failed to connect device\n");
|
|
return -1;
|
|
}
|
|
ret = client.queryConnect();
|
|
if (ret == NO_ERROR) {
|
|
printf("Connected to device\n");
|
|
} else {
|
|
printf("Failed to connect device\n");
|
|
return -1;
|
|
}
|
|
// Caputre ASAP
|
|
if (v1) {
|
|
//ret = client.queryStart();
|
|
ret = client.queryStart(pixFmt, width, height);
|
|
} else {
|
|
ret = client.queryStart(pixFmt, width, height);
|
|
}
|
|
if (ret != NO_ERROR) {
|
|
printf("Failed to configure device for query\n");
|
|
return -1;
|
|
}
|
|
if (v1) {
|
|
const uint64_t usage =
|
|
GRALLOC_USAGE_HW_CAMERA_READ |
|
|
GRALLOC_USAGE_HW_CAMERA_WRITE |
|
|
GRALLOC_USAGE_HW_TEXTURE;
|
|
uint32_t stride;
|
|
|
|
buffer_handle_t handle;
|
|
if (GraphicBufferAllocator::get().allocate(
|
|
width, height, uiFmt, 1, usage,
|
|
&handle, &stride,
|
|
0, "EmulatorCameraTest") != ::android::OK) {
|
|
printf("GraphicBufferAllocator::allocate failed\n");
|
|
return -1;
|
|
}
|
|
|
|
void* addr;
|
|
if (uiFmt == HAL_PIXEL_FORMAT_RGBA_8888) {
|
|
GraphicBufferMapper::get().lock(
|
|
handle,
|
|
GRALLOC_USAGE_HW_CAMERA_WRITE,
|
|
Rect(0, 0, width, height),
|
|
&addr);
|
|
} else {
|
|
android_ycbcr ycbcr;
|
|
GraphicBufferMapper::get().lockYCbCr(
|
|
handle,
|
|
GRALLOC_USAGE_HW_CAMERA_WRITE,
|
|
Rect(0, 0, width, height),
|
|
&ycbcr);
|
|
addr = ycbcr.y;
|
|
}
|
|
|
|
const cb_handle_t* cbHandle = cb_handle_t::from(handle);
|
|
|
|
uint64_t offset = cbHandle->getMmapedOffset();
|
|
printf("offset is 0x%llx\n", offset);
|
|
float whiteBalance[] = {1.0f, 1.0f, 1.0f};
|
|
float exposureCompensation = 1.0f;
|
|
for (int i = 0 ; i < repeated; i++) {
|
|
nsecs_t start = systemTime();
|
|
client.queryFrame(width, height, pixFmt, offset,
|
|
whiteBalance[0], whiteBalance[1], whiteBalance[2],
|
|
exposureCompensation, nullptr);
|
|
nsecs_t end = systemTime();
|
|
report.push_back(end - start);
|
|
}
|
|
GraphicBufferMapper::get().unlock(handle);
|
|
GraphicBufferAllocator::get().free(handle);
|
|
} else {
|
|
size_t bufferSize;
|
|
if (pixFmt == V4L2_PIX_FMT_RGB32) {
|
|
bufferSize = width * height * 4;
|
|
} else {
|
|
bufferSize = width * height * 12 / 8;
|
|
}
|
|
std::vector<char> buffer(bufferSize, 0);
|
|
float whiteBalance[] = {1.0f, 1.0f, 1.0f};
|
|
float exposureCompensation = 1.0f;
|
|
for (int i = 0 ; i < repeated; i++) {
|
|
nsecs_t start = systemTime();
|
|
client.queryFrame(buffer.data(), nullptr, 0, bufferSize,
|
|
whiteBalance[0], whiteBalance[1], whiteBalance[2],
|
|
exposureCompensation, nullptr);
|
|
nsecs_t end = systemTime();
|
|
report.push_back(end - start);
|
|
}
|
|
}
|
|
}
|
|
// Report
|
|
nsecs_t average, sum = 0;
|
|
for (int i = 0; i < repeated; i++) {
|
|
sum += report[i];
|
|
}
|
|
average = sum / repeated;
|
|
printf("Report for reading %d frames\n", repeated);
|
|
printf("\ttime total: %lld\n", sum);
|
|
printf("\tframe average: %lld\n", average);
|
|
|
|
return 0;
|
|
}
|
|
|