# SPDX-License-Identifier: BSD-2-Clause # # Copyright (C) 2019, Raspberry Pi (Trading) Limited # # ctt_noise.py - camera tuning tool noise calibration from ctt_image_load import * import matplotlib.pyplot as plt """ Find noise standard deviation and fit to model: noise std = a + b*sqrt(pixel mean) """ def noise(Cam, Img, plot): Cam.log += '\nProcessing image: {}'.format(Img.name) stds = [] means = [] """ iterate through macbeth square patches """ for ch_patches in Img.patches: for patch in ch_patches: """ renormalise patch """ patch = np.array(patch) patch = (patch-Img.blacklevel_16)/Img.againQ8_norm std = np.std(patch) mean = np.mean(patch) stds.append(std) means.append(mean) """ clean data and ensure all means are above 0 """ stds = np.array(stds) means = np.array(means) means = np.clip(np.array(means), 0, None) sq_means = np.sqrt(means) """ least squares fit model """ fit = np.polyfit(sq_means, stds, 1) Cam.log += '\nBlack level = {}'.format(Img.blacklevel_16) Cam.log += '\nNoise profile: offset = {}'.format(int(fit[1])) Cam.log += ' slope = {:.3f}'.format(fit[0]) """ remove any values further than std from the fit anomalies most likely caused by: > ucharacteristically noisy white patch > saturation in the white patch """ fit_score = np.abs(stds - fit[0]*sq_means - fit[1]) fit_std = np.std(stds) fit_score_norm = fit_score - fit_std anom_ind = np.where(fit_score_norm > 1) fit_score_norm.sort() sq_means_clean = np.delete(sq_means, anom_ind) stds_clean = np.delete(stds, anom_ind) removed = len(stds) - len(stds_clean) if removed != 0: Cam.log += '\nIdentified and removed {} anomalies.'.format(removed) Cam.log += '\nRecalculating fit' """ recalculate fit with outliers removed """ fit = np.polyfit(sq_means_clean, stds_clean, 1) Cam.log += '\nNoise profile: offset = {}'.format(int(fit[1])) Cam.log += ' slope = {:.3f}'.format(fit[0]) """ if fit const is < 0 then force through 0 by dividing by sq_means and fitting poly order 0 """ corrected = 0 if fit[1] < 0: corrected = 1 ones = np.ones(len(means)) y_data = stds/sq_means fit2 = np.polyfit(ones, y_data, 0) Cam.log += '\nOffset below zero. Fit recalculated with zero offset' Cam.log += '\nNoise profile: offset = 0' Cam.log += ' slope = {:.3f}'.format(fit2[0]) # print('new fit') # print(fit2) """ plot fit for debug """ if plot: x = np.arange(sq_means.max()//0.88) fit_plot = x*fit[0] + fit[1] plt.scatter(sq_means, stds, label='data', color='blue') plt.scatter(sq_means[anom_ind], stds[anom_ind], color='orange', label='anomalies') plt.plot(x, fit_plot, label='fit', color='red', ls=':') if fit[1] < 0: fit_plot_2 = x*fit2[0] plt.plot(x, fit_plot_2, label='fit 0 intercept', color='green', ls='--') plt.plot(0, 0) plt.title('Noise Plot\nImg: {}'.format(Img.str)) plt.legend(loc='upper left') plt.xlabel('Sqrt Pixel Value') plt.ylabel('Noise Standard Deviation') plt.grid() plt.show() """ End of plotting code """ """ format output to include forced 0 constant """ Cam.log += '\n' if corrected: fit = [fit2[0], 0] return fit else: return fit > 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 105 106 107 108 109 110 111 112 113
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2019, Raspberry Pi (Trading) Limited
*
* cam_helper.cpp - helper information for different sensors
*/
#include <linux/videodev2.h>
#include <assert.h>
#include <map>
#include <string.h>
#include "libcamera/internal/v4l2_videodevice.h"
#include "cam_helper.hpp"
#include "md_parser.hpp"
using namespace RPiController;
static std::map<std::string, CamHelperCreateFunc> cam_helpers;
CamHelper *CamHelper::Create(std::string const &cam_name)
{
/*
* CamHelpers get registered by static RegisterCamHelper
* initialisers.
*/
for (auto &p : cam_helpers) {
if (cam_name.find(p.first) != std::string::npos)
return p.second();
}
return nullptr;
}
CamHelper::CamHelper(MdParser *parser)
: parser_(parser), initialized_(false)
{
}
CamHelper::~CamHelper()
{
delete parser_;
}
uint32_t CamHelper::ExposureLines(double exposure_us) const
{
assert(initialized_);
return exposure_us * 1000.0 / mode_.line_length;
}
double CamHelper::Exposure(uint32_t exposure_lines) const
{
assert(initialized_);
return exposure_lines * mode_.line_length / 1000.0;
}
void CamHelper::SetCameraMode(const CameraMode &mode)
{
mode_ = mode;
parser_->SetBitsPerPixel(mode.bitdepth);
parser_->SetLineLengthBytes(0); /* We use SetBufferSize. */
initialized_ = true;
}
void CamHelper::GetDelays(int &exposure_delay, int &gain_delay) const
{
/*
* These values are correct for many sensors. Other sensors will
* need to over-ride this method.
*/
exposure_delay = 2;
gain_delay = 1;
}
bool CamHelper::SensorEmbeddedDataPresent() const
{
return false;
}
unsigned int CamHelper::HideFramesStartup() const
{
/*
* The number of frames when a camera first starts that shouldn't be
* displayed as they are invalid in some way.
*/
return 0;
}
unsigned int CamHelper::HideFramesModeSwitch() const
{
/* After a mode switch, many sensors return valid frames immediately. */
return 0;
}
unsigned int CamHelper::MistrustFramesStartup() const
{
/* Many sensors return a single bad frame on start-up. */
return 1;
}
unsigned int CamHelper::MistrustFramesModeSwitch() const
{
/* Many sensors return valid metadata immediately. */
return 0;
}
RegisterCamHelper::RegisterCamHelper(char const *cam_name,
CamHelperCreateFunc create_func)
{
cam_helpers[std::string(cam_name)] = create_func;
}