summaryrefslogtreecommitdiff
path: root/utils/raspberrypi/ctt/ctt_noise.py
blob: 0afcf8f850ccf688068379f2b9b25d463baeb4d6 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# 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
>const std::string &path) { if (path.empty()) return "."; /* * Skip all trailing slashes. If the path is only made of slashes, * return "/". */ size_t pos = path.size() - 1; while (path[pos] == '/') { if (!pos) return "/"; pos--; } /* * Find the previous slash. If the path contains no non-trailing slash, * return ".". */ while (path[pos] != '/') { if (!pos) return "."; pos--; } /* * Return the directory name up to (but not including) any trailing * slash. If this would result in an empty string, return "/". */ while (path[pos] == '/') { if (!pos) return "/"; pos--; } return path.substr(0, pos + 1); } /** * \fn std::vector<typename T::key_type> map_keys(const T &map) * \brief Retrieve the keys of a std::map<> * \param[in] map The map whose keys to retrieve * \return A std::vector<> containing the keys of \a map */ /** * \fn libcamera::utils::set_overlap(InputIt1 first1, InputIt1 last1, * InputIt2 first2, InputIt2 last2) * \brief Count the number of elements in the intersection of two ranges * * Count the number of elements in the intersection of the sorted ranges [\a * first1, \a last1) and [\a first1, \a last2). Elements are compared using * operator< and the ranges must be sorted with respect to the same. * * \return The number of elements in the intersection of the two ranges */ /** * \typedef clock * \brief The libcamera clock (monotonic) */ /** * \typedef duration * \brief The libcamera duration related to libcamera::utils::clock */ /** * \typedef time_point * \brief The libcamera time point related to libcamera::utils::clock */ /** * \brief Convert a duration to a timespec * \param[in] value The duration * \return A timespec expressing the duration */ struct timespec duration_to_timespec(const duration &value) { uint64_t nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(value).count(); struct timespec ts; ts.tv_sec = nsecs / 1000000000ULL; ts.tv_nsec = nsecs % 1000000000ULL; return ts; } /** * \brief Convert a time point to a string representation * \param[in] time The time point * \return A string representing the time point in hh:mm:ss.nanoseconds format */ std::string time_point_to_string(const time_point &time) { uint64_t nsecs = std::chrono::duration_cast<std::chrono::nanoseconds>(time.time_since_epoch()).count(); unsigned int secs = nsecs / 1000000000ULL; std::ostringstream ossTimestamp; ossTimestamp.fill('0'); ossTimestamp << secs / (60 * 60) << ":" << std::setw(2) << (secs / 60) % 60 << ":" << std::setw(2) << secs % 60 << "." << std::setw(9) << nsecs % 1000000000ULL; return ossTimestamp.str(); } std::basic_ostream<char, std::char_traits<char>> & operator<<(std::basic_ostream<char, std::char_traits<char>> &stream, const _hex &h) { stream << "0x"; std::ostream::fmtflags flags = stream.setf(std::ios_base::hex, std::ios_base::basefield); std::streamsize width = stream.width(h.w); char fill = stream.fill('0'); stream << h.v; stream.flags(flags); stream.width(width); stream.fill(fill); return stream; } /** * \fn hex(T value, unsigned int width) * \brief Write an hexadecimal value to an output string * \param value The value * \param width The width * * Return an object of unspecified type such that, if \a os is the name of an * output stream of type std::ostream, and T is an integer type, then the * expression * * \code{.cpp} * os << utils::hex(value) * \endcode * * will output the \a value to the stream in hexadecimal form with the base * prefix and the filling character set to '0'. The field width is set to \a * width if specified to a non-zero value, or to the native width of type T * otherwise. The \a os stream configuration is not modified. */ /** * \brief Copy a string with a size limit * \param[in] dst The destination string * \param[in] src The source string * \param[in] size The size of the destination string * * This function copies the null-terminated string \a src to \a dst with a limit * of \a size - 1 characters, and null-terminates the result if \a size is * larger than 0. If \a src is larger than \a size - 1, \a dst is truncated. * * \return The size of \a src */ size_t strlcpy(char *dst, const char *src, size_t size) { if (size) { strncpy(dst, src, size); dst[size - 1] = '\0'; } return strlen(src); } details::StringSplitter::StringSplitter(const std::string &str, const std::string &delim) : str_(str), delim_(delim) { } details::StringSplitter::iterator::iterator(const details::StringSplitter *ss, std::string::size_type pos) : ss_(ss), pos_(pos) { next_ = ss_->str_.find(ss_->delim_, pos_); } details::StringSplitter::iterator &details::StringSplitter::iterator::operator++() { pos_ = next_; if (pos_ != std::string::npos) { pos_ += ss_->delim_.length(); next_ = ss_->str_.find(ss_->delim_, pos_); } return *this; } std::string details::StringSplitter::iterator::operator*() const { std::string::size_type count; count = next_ != std::string::npos ? next_ - pos_ : next_; return ss_->str_.substr(pos_, count); } bool details::StringSplitter::iterator::operator!=(const details::StringSplitter::iterator &other) const { return pos_ != other.pos_; } details::StringSplitter::iterator details::StringSplitter::begin() const { return iterator(this, 0); } details::StringSplitter::iterator details::StringSplitter::end() const { return iterator(this, std::string::npos); } /** * \fn template<typename Container, typename UnaryOp> \ * std::string utils::join(const Container &items, const std::string &sep, UnaryOp op) * \brief Join elements of a container in a string with a separator * \param[in] items The container * \param[in] sep The separator to add between elements * \param[in] op A function that converts individual elements to strings * * This function joins all elements in the \a items container into a string and * returns it. The \a sep separator is added between elements. If the container * elements are not implicitly convertible to std::string, the \a op function * shall be provided to perform conversion of elements to std::string. * * \return A string that concatenates all elements in the container */ /** * \fn split(const std::string &str, const std::string &delim) * \brief Split a string based on a delimiter * \param[in] str The string to split * \param[in] delim The delimiter string * * This function splits the string \a str into substrings based on the * delimiter \a delim. It returns an object of unspecified type that can be * used in a range-based for loop and yields the substrings in sequence. * * \return An object that can be used in a range-based for loop to iterate over * the substrings */ details::StringSplitter split(const std::string &str, const std::string &delim) { /** \todo Try to avoid copies of str and delim */ return details::StringSplitter(str, delim); } /** * \brief Remove any non-ASCII characters from a string * \param[in] str The string to strip * * Remove all non-ASCII characters from a string. * * \return A string equal to \a str stripped out of all non-ASCII characters */ std::string toAscii(const std::string &str) { std::string ret; for (const char &c : str) if (!(c & 0x80)) ret += c; return ret; } /** * \brief Check if libcamera is installed or not * * Utilise the build_rpath dynamic tag which is stripped out by meson at * install time to determine at runtime if the library currently executing * has been installed or not. * * \return True if libcamera is installed, false otherwise */ bool isLibcameraInstalled() { /* * DT_RUNPATH (DT_RPATH when the linker uses old dtags) is removed on * install. */ for (const ElfW(Dyn) *dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn) { if (dyn->d_tag == DT_RUNPATH || dyn->d_tag == DT_RPATH) return false; } return true; } /** * \brief Retrieve the path to the build directory * * During development, it is useful to run libcamera binaries directly from the * build directory without installing them. This function helps components that * need to locate resources in the build tree, such as IPA modules or IPA proxy * workers, by providing them with the path to the root of the build directory. * Callers can then use it to complement or override searches in system-wide * directories. * * If libcamera has been installed, the build directory path is not available * and this function returns an empty string. * * \return The path to the build directory if running from a build, or an empty * string otherwise */ std::string libcameraBuildPath() { if (isLibcameraInstalled()) return std::string(); Dl_info info; /* Look up our own symbol. */ int ret = dladdr(reinterpret_cast<void *>(libcameraBuildPath), &info); if (ret == 0) return std::string(); std::string path = dirname(info.dli_fname) + "/../../"; char *real = realpath(path.c_str(), nullptr); if (!real) return std::string(); path = real; free(real); return path + "/"; } /** * \brief Retrieve the path to the source directory * * During development, it is useful to run libcamera binaries directly from the * build directory without installing them. This function helps components that * need to locate resources in the source tree, such as IPA configuration * files, by providing them with the path to the root of the source directory. * Callers can then use it to complement or override searches in system-wide * directories. * * If libcamera has been installed, the source directory path is not available * and this function returns an empty string. * * \return The path to the source directory if running from a build directory, * or an empty string otherwise */ std::string libcameraSourcePath() { std::string path = libcameraBuildPath(); if (path.empty()) return std::string(); path += "source"; char *real = realpath(path.c_str(), nullptr); if (!real) return std::string(); path = real; free(real); struct stat statbuf; int ret = stat(path.c_str(), &statbuf); if (ret < 0 || (statbuf.st_mode & S_IFMT) != S_IFDIR) return std::string(); return path + "/"; } /** * \fn alignDown(unsigned int value, unsigned int alignment) * \brief Align \a value down to \a alignment * \param[in] value The value to align * \param[in] alignment The alignment * \return The value rounded down to the nearest multiple of \a alignment */ /** * \fn alignUp(unsigned int value, unsigned int alignment) * \brief Align \a value up to \a alignment * \param[in] value The value to align * \param[in] alignment The alignment * \return The value rounded up to the nearest multiple of \a alignment */ } /* namespace utils */ } /* namespace libcamera */