summaryrefslogtreecommitdiff
path: root/src/ipa/raspberrypi/controller/metadata.hpp
blob: 4f44ffc6771c34633df6b485999f49a436c13fec (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
/* SPDX-License-Identifier: BSD-2-Clause */
/*
 * Copyright (C) 2019, Raspberry Pi (Trading) Limited
 *
 * metadata.hpp - general metadata class
 */
#pragma once

// A simple class for carrying arbitrary metadata, for example about an image.

#include <any>
#include <string>
#include <mutex>
#include <map>
#include <memory>

namespace RPiController {

class Metadata
{
public:
	template<typename T> void Set(std::string const &tag, T const &value)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		data_[tag] = value;
	}
	template<typename T> int Get(std::string const &tag, T &value) const
	{
		std::lock_guard<std::mutex> lock(mutex_);
		auto it = data_.find(tag);
		if (it == data_.end())
			return -1;
		value = std::any_cast<T>(it->second);
		return 0;
	}
	void Clear()
	{
		std::lock_guard<std::mutex> lock(mutex_);
		data_.clear();
	}
	Metadata &operator=(Metadata const &other)
	{
		std::lock_guard<std::mutex> lock(mutex_);
		std::lock_guard<std::mutex> other_lock(other.mutex_);
		data_ = other.data_;
		return *this;
	}
	template<typename T> T *GetLocked(std::string const &tag)
	{
		// This allows in-place access to the Metadata contents,
		// for which you should be holding the lock.
		auto it = data_.find(tag);
		if (it == data_.end())
			return nullptr;
		return std::any_cast<T>(&it->second);
	}
	template<typename T>
	void SetLocked(std::string const &tag, T const &value)
	{
		// Use this only if you're holding the lock yourself.
		data_[tag] = value;
	}
	// Note: use of (lowercase) lock and unlock means you can create scoped
	// locks with the standard lock classes.
	// e.g. std::lock_guard<PisP::Metadata> lock(metadata)
	void lock() { mutex_.lock(); }
	void unlock() { mutex_.unlock(); }

private:
	mutable std::mutex mutex_;
	std::map<std::string, std::any> data_;
};

typedef std::shared_ptr<Metadata> MetadataPtr;

} // namespace RPiController
metadata[f'Exif.{photo}.ISOSpeedRatings'].value * 256 / 100 self.againQ8_norm = self.againQ8 / 256 self.camName = metadata['Exif.Image.Model'].value self.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0]) self.blacklevel_16 = self.blacklevel << (16 - self.sigbits) # Channel order depending on bayer pattern # The key is the order given by exif, where 0 is R, 1 is G, and 2 is B # The value is the index where the color can be found, where the first # is R, then G, then G, then B. bayer_case = { '0 1 1 2': (lt.Color.R, lt.Color.GR, lt.Color.GB, lt.Color.B), '1 2 0 1': (lt.Color.GB, lt.Color.R, lt.Color.B, lt.Color.GR), '2 1 1 0': (lt.Color.B, lt.Color.GB, lt.Color.GR, lt.Color.R), '1 0 2 1': (lt.Color.GR, lt.Color.R, lt.Color.B, lt.Color.GB) } # Note: This needs to be in IFD0 cfa_pattern = metadata[f'Exif.{subimage}.CFAPattern'].value self.order = bayer_case[cfa_pattern] def _read_image_dng(self): raw_im = raw.imread(str(self.path)) raw_data = raw_im.raw_image shift = 16 - self.sigbits c0 = np.left_shift(raw_data[0::2, 0::2].astype(np.int64), shift) c1 = np.left_shift(raw_data[0::2, 1::2].astype(np.int64), shift) c2 = np.left_shift(raw_data[1::2, 0::2].astype(np.int64), shift) c3 = np.left_shift(raw_data[1::2, 1::2].astype(np.int64), shift) self.channels = [c0, c1, c2, c3] # Reorder the channels into R, GR, GB, B self.channels = [self.channels[i] for i in self.order] # \todo Move this to macbeth.py def get_patches(self, cen_coords, size=16): saturated = False # Obtain channel widths and heights ch_w, ch_h = self.w, self.h cen_coords = list(np.array((cen_coords[0])).astype(np.int32)) self.cen_coords = cen_coords # Squares are ordered by stacking macbeth chart columns from left to # right. Some useful patch indices: # white = 3 # black = 23 # 'reds' = 9, 10 # 'blues' = 2, 5, 8, 20, 22 # 'greens' = 6, 12, 17 # greyscale = 3, 7, 11, 15, 19, 23 all_patches = [] for ch in self.channels: ch_patches = [] for cen in cen_coords: # Macbeth centre is placed at top left of central 2x2 patch to # account for rounding. Patch pixels are sorted by pixel # brightness so spatial information is lost. patch = ch[cen[1] - 7:cen[1] + 9, cen[0] - 7:cen[0] + 9].flatten() patch.sort() if patch[-5] == (2**self.sigbits - 1) * 2**(16 - self.sigbits): saturated = True ch_patches.append(patch) all_patches.append(ch_patches) self.patches = all_patches return not saturated