summaryrefslogtreecommitdiff
path: root/src/ipa/raspberrypi/controller/rpi/lux.cpp
blob: 154db153b11913a2929b9f42b067880ea6c87b66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (C) 2019, Raspberry Pi (Trading) Limited
 *
 * lux.cpp - Lux control algorithm
 */
#include <math.h>

#include "linux/bcm2835-isp.h"

#include "../device_status.h"
#include "../logging.hpp"

#include "lux.hpp"

using namespace RPi;

#define NAME "rpi.lux"

Lux::Lux(Controller *controller)
	: Algorithm(controller)
{
	// Put in some defaults as there will be no meaningful values until
	// Process has run.
	status_.aperture = 1.0;
	status_.lux = 400;
}

char const *Lux::Name() const
{
	return NAME;
}

void Lux::Read(boost::property_tree::ptree const &params)
{
	RPI_LOG(Name());
	reference_shutter_speed_ =
		params.get<double>("reference_shutter_speed");
	reference_gain_ = params.get<double>("reference_gain");
	reference_aperture_ = params.get<double>("reference_aperture", 1.0);
	reference_Y_ = params.get<double>("reference_Y");
	reference_lux_ = params.get<double>("reference_lux");
	current_aperture_ = reference_aperture_;
}

void Lux::Prepare(Metadata *image_metadata)
{
	std::unique_lock<std::mutex> lock(mutex_);
	image_metadata->Set("lux.status", status_);
}

void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata)
{
	// set some initial values to shut the compiler up
	DeviceStatus device_status =
		{ .shutter_speed = 1.0,
		  .analogue_gain = 1.0,
		  .lens_position = 0.0,
		  .aperture = 0.0,
		  .flash_intensity = 0.0 };
	if (image_metadata->Get("device.status", device_status) == 0) {
		double current_gain = device_status.analogue_gain;
		double current_shutter_speed = device_status.shutter_speed;
		double current_aperture = device_status.aperture;
		if (current_aperture == 0)
			current_aperture = current_aperture_;
		uint64_t sum = 0;
		uint32_t num = 0;
		uint32_t *bin = stats->hist[0].g_hist;
		const int num_bins = sizeof(stats->hist[0].g_hist) /
				     sizeof(stats->hist[0].g_hist[0]);
		for (int i = 0; i < num_bins; i++)
			sum += bin[i] * (uint64_t)i, num += bin[i];
		// add .5 to reflect the mid-points of bins
		double current_Y = sum / (double)num + .5;
		double gain_ratio = reference_gain_ / current_gain;
		double shutter_speed_ratio =
			reference_shutter_speed_ / current_shutter_speed;
		double aperture_ratio = reference_aperture_ / current_aperture;
		double Y_ratio = current_Y * (65536 / num_bins) / reference_Y_;
		double estimated_lux = shutter_speed_ratio * gain_ratio *
				       aperture_ratio * aperture_ratio *
				       Y_ratio * reference_lux_;
		LuxStatus status;
		status.lux = estimated_lux;
		status.aperture = current_aperture;
		RPI_LOG(Name() << ": estimated lux " << estimated_lux);
		{
			std::unique_lock<std::mutex> lock(mutex_);
			status_ = status;
		}
		// Overwrite the metadata here as well, so that downstream
		// algorithms get the latest value.
		image_metadata->Set("lux.status", status);
	} else
		RPI_WARN(Name() << ": no device metadata");
}

// Register algorithm with the system.
static Algorithm *Create(Controller *controller)
{
	return (Algorithm *)new Lux(controller);
}
static RegisterAlgorithm reg(NAME, &Create);
n class="hl opt">, this); } IPAContextWrapper::~IPAContextWrapper() { if (!ctx_) return; ctx_->ops->destroy(ctx_); } int IPAContextWrapper::init(const IPASettings &settings) { if (intf_) return intf_->init(settings); if (!ctx_) return 0; struct ipa_settings c_settings; c_settings.configuration_file = settings.configurationFile.c_str(); ctx_->ops->init(ctx_, &c_settings); return 0; } int IPAContextWrapper::start(const IPAOperationData &data, IPAOperationData *result) { if (intf_) return intf_->start(data, result); if (!ctx_) return 0; return ctx_->ops->start(ctx_); } void IPAContextWrapper::stop() { if (intf_) return intf_->stop(); if (!ctx_) return; ctx_->ops->stop(ctx_); } void IPAContextWrapper::configure(const CameraSensorInfo &sensorInfo, const std::map<unsigned int, IPAStream> &streamConfig, const std::map<unsigned int, const ControlInfoMap &> &entityControls, const IPAOperationData &ipaConfig, IPAOperationData *result) { if (intf_) return intf_->configure(sensorInfo, streamConfig, entityControls, ipaConfig, result); if (!ctx_) return; serializer_.reset(); /* Translate the camera sensor info. */ struct ipa_sensor_info sensor_info = {}; sensor_info.model = sensorInfo.model.c_str(); sensor_info.bits_per_pixel = sensorInfo.bitsPerPixel; sensor_info.active_area.width = sensorInfo.activeAreaSize.width; sensor_info.active_area.height = sensorInfo.activeAreaSize.height; sensor_info.analog_crop.left = sensorInfo.analogCrop.x; sensor_info.analog_crop.top = sensorInfo.analogCrop.y; sensor_info.analog_crop.width = sensorInfo.analogCrop.width; sensor_info.analog_crop.height = sensorInfo.analogCrop.height; sensor_info.output_size.width = sensorInfo.outputSize.width; sensor_info.output_size.height = sensorInfo.outputSize.height; sensor_info.pixel_rate = sensorInfo.pixelRate; sensor_info.line_length = sensorInfo.lineLength; /* Translate the IPA stream configurations map. */ struct ipa_stream c_streams[streamConfig.size()]; unsigned int i = 0; for (const auto &stream : streamConfig) { struct ipa_stream *c_stream = &c_streams[i]; unsigned int id = stream.first; const IPAStream &ipaStream = stream.second; c_stream->id = id; c_stream->pixel_format = ipaStream.pixelFormat; c_stream->width = ipaStream.size.width; c_stream->height = ipaStream.size.height; ++i; } /* Translate the IPA entity controls map. */ struct ipa_control_info_map c_info_maps[entityControls.size()]; std::vector<std::vector<uint8_t>> data(entityControls.size()); i = 0; for (const auto &info : entityControls) { struct ipa_control_info_map &c_info_map = c_info_maps[i]; unsigned int id = info.first; const ControlInfoMap &infoMap = info.second; size_t infoMapSize = serializer_.binarySize(infoMap); data[i].resize(infoMapSize); ByteStreamBuffer byteStream(data[i].data(), data[i].size()); serializer_.serialize(infoMap, byteStream); c_info_map.id = id; c_info_map.data = byteStream.base(); c_info_map.size = byteStream.size(); ++i; } /* \todo Translate the ipaConfig and reponse */ ctx_->ops->configure(ctx_, &sensor_info, c_streams, streamConfig.size(), c_info_maps, entityControls.size()); } void IPAContextWrapper::mapBuffers(const std::vector<IPABuffer> &buffers) { if (intf_) return intf_->mapBuffers(buffers); if (!ctx_) return; struct ipa_buffer c_buffers[buffers.size()]; for (unsigned int i = 0; i < buffers.size(); ++i) { struct ipa_buffer &c_buffer = c_buffers[i]; const IPABuffer &buffer = buffers[i]; const std::vector<FrameBuffer::Plane> &planes = buffer.planes; c_buffer.id = buffer.id; c_buffer.num_planes = planes.size(); for (unsigned int j = 0; j < planes.size(); ++j) { const FrameBuffer::Plane &plane = planes[j]; c_buffer.planes[j].dmabuf = plane.fd.fd(); c_buffer.planes[j].length = plane.length; } } ctx_->ops->map_buffers(ctx_, c_buffers, buffers.size()); } void IPAContextWrapper::unmapBuffers(const std::vector<unsigned int> &ids) { if (intf_) return intf_->unmapBuffers(ids); if (!ctx_) return; ctx_->ops->unmap_buffers(ctx_, ids.data(), ids.size()); } void IPAContextWrapper::processEvent(const IPAOperationData &data) { if (intf_) return intf_->processEvent(data); if (!ctx_) return; struct ipa_operation_data c_data; c_data.operation = data.operation; c_data.data = data.data.data(); c_data.num_data = data.data.size(); struct ipa_control_list control_lists[data.controls.size()]; c_data.lists = control_lists; c_data.num_lists = data.controls.size(); std::size_t listsSize = 0; for (const auto &list : data.controls) listsSize += serializer_.binarySize(list); std::vector<uint8_t> binaryData(listsSize); ByteStreamBuffer byteStreamBuffer(binaryData.data(), listsSize); unsigned int i = 0; for (const auto &list : data.controls) { struct ipa_control_list &c_list = control_lists[i]; c_list.size = serializer_.binarySize(list); ByteStreamBuffer b = byteStreamBuffer.carveOut(c_list.size); serializer_.serialize(list, b); c_list.data = b.base(); } ctx_->ops->process_event(ctx_, &c_data); } void IPAContextWrapper::doQueueFrameAction(unsigned int frame, const IPAOperationData &data) { IPAInterface::queueFrameAction.emit(frame, data); } void IPAContextWrapper::queue_frame_action(void *ctx, unsigned int frame, struct ipa_operation_data &data) { IPAContextWrapper *_this = static_cast<IPAContextWrapper *>(ctx); IPAOperationData opData; opData.operation = data.operation; for (unsigned int i = 0; i < data.num_data; ++i) opData.data.push_back(data.data[i]); for (unsigned int i = 0; i < data.num_lists; ++i) { const struct ipa_control_list &c_list = data.lists[i]; ByteStreamBuffer b(c_list.data, c_list.size); opData.controls.push_back(_this->serializer_.deserialize<ControlList>(b)); } _this->doQueueFrameAction(frame, opData); } #ifndef __DOXYGEN__ /* * This construct confuses Doxygen and makes it believe that all members of the * operations is a member of IPAContextWrapper. It must thus be hidden. */ const struct ipa_callback_ops IPAContextWrapper::callbacks_ = { .queue_frame_action = &IPAContextWrapper::queue_frame_action, }; #endif } /* namespace libcamera */