diff --git a/.env.dev b/.env.dev new file mode 100644 index 0000000..eb19e75 --- /dev/null +++ b/.env.dev @@ -0,0 +1 @@ +export PROJECT_ROOT_DIR=/home/l_bechir/github/aare diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 930311e..67bb44b 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,7 +3,10 @@ { "name": "Linux", "includePath": [ - "${workspaceFolder}/**", + "${workspaceFolder}/file_io/**", + "${workspaceFolder}/core/**", + "${workspaceFolder}/tests/**", + "${workspaceFolder}/tests/**", "/usr/include" ], "defines": [], diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c4b85..e000e6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,4 +86,4 @@ target_link_libraries(example PUBLIC aare) if(AARE_PYTHON_BINDINGS) add_subdirectory(python) -endif() \ No newline at end of file +endif() diff --git a/core/include/aare/Frame.hpp b/core/include/aare/Frame.hpp index 252103a..41c9b68 100644 --- a/core/include/aare/Frame.hpp +++ b/core/include/aare/Frame.hpp @@ -19,8 +19,10 @@ template class Frame { ssize_t m_bitdepth = sizeof(DataType) * 8; public: + Frame(ssize_t rows, ssize_t cols); Frame(std::byte *fp, ssize_t rows, ssize_t cols); DataType get(int row, int col); + std::vector> get_array(); ssize_t rows() const{ return m_rows; } @@ -30,6 +32,9 @@ template class Frame { ssize_t bitdepth() const{ return m_bitdepth; } + DataType* _get_data(){ + return m_data; + } ~Frame() { delete[] m_data; } }; diff --git a/core/src/Frame.cpp b/core/src/Frame.cpp index 97a7b81..3d4e940 100644 --- a/core/src/Frame.cpp +++ b/core/src/Frame.cpp @@ -8,6 +8,13 @@ Frame::Frame(std::byte* bytes, ssize_t rows, ssize_t cols): std::memcpy(m_data, bytes, m_rows*m_cols*sizeof(DataType)); } +template +Frame::Frame(ssize_t rows, ssize_t cols): + m_rows(rows), m_cols(cols) { + m_data = new DataType[m_rows*m_cols]; + } + + template DataType Frame::get(int row, int col) { @@ -18,5 +25,17 @@ DataType Frame::get(int row, int col) { return m_data[row*m_cols + col]; } +template +std::vector> Frame::get_array() { + std::vector> array; + for (int i = 0; i < m_rows; i++) { + std::vector row; + row.assign(m_data + i*m_cols, m_data + (i+1)*m_cols); + array.push_back(row); + } + + return array; +} + template class Frame; diff --git a/data/test_numpy_file.npy b/data/test_numpy_file.npy new file mode 100644 index 0000000..ca1bc7e Binary files /dev/null and b/data/test_numpy_file.npy differ diff --git a/examples/main.cpp b/examples/main.cpp index f4d45cb..a1464e0 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -1,25 +1,35 @@ // Your First C++ Program -#include #include "aare/FileHandler.hpp" +#include -using JFileHandler = FileHandler; -using JFile = File; +#define AARE_ROOT_DIR_VAR "PROJECT_ROOT_DIR" + +using JFileHandler = FileHandler; +using JFile = File; using JFrame = Frame; - -void test(JFileHandler* f,int frame_number){ +void test(JFileHandler *f, int frame_number) { std::cout << "frame number: " << frame_number << std::endl; - JFrame* frame = f->get_frame(frame_number); - std::cout << frame->get(0,0) << ' '; - std::cout << frame->get(0,1) << ' '; - std::cout << frame->get(1,0) << ' '; - std::cout << frame->get(511,1023) << std::endl; + JFrame *frame = f->get_frame(frame_number); + std::cout << frame->get(0, 0) << std::endl; + std::cout << frame->get(0, 1) << std::endl; + std::cout << frame->get(1, 0) << std::endl; + std::cout << frame->get(49, 49) << std::endl; delete frame; } int main() { - std::filesystem::path fpath("/home/bb/github/aare/data/jungfrau_single_master_0.json"); - auto fileHandler = new JFileHandler (fpath); + auto PROJECT_ROOT_DIR = std::filesystem::path(getenv(AARE_ROOT_DIR_VAR)); + // std::filesystem::path fpath("/home/bb/github/aare/data/jungfrau_single_master_0.json"); + std::filesystem::path fpath(PROJECT_ROOT_DIR / "data" / "test_numpy_file.npy"); + std::cout<* get_frame(int frame_number) = 0; private: + //comment + public: virtual ~File() = default; diff --git a/file_io/include/aare/JsonFileFactory.hpp b/file_io/include/aare/JsonFileFactory.hpp index a65453c..4c0ffac 100644 --- a/file_io/include/aare/JsonFileFactory.hpp +++ b/file_io/include/aare/JsonFileFactory.hpp @@ -5,11 +5,10 @@ class JsonFileFactory: public FileFactory private: /* data */ public: + JsonFileFactory(std::filesystem::path fpath); File* load_file() override; void parse_metadata(File*) override; void parse_fname(File*) override; - - JsonFileFactory(std::filesystem::path fpath); void open_subfiles(File*); sls_detector_header read_header(const std::filesystem::path &fname); void find_geometry(File*); diff --git a/file_io/include/aare/NumpyFile.hpp b/file_io/include/aare/NumpyFile.hpp index 1230af9..33ab769 100644 --- a/file_io/include/aare/NumpyFile.hpp +++ b/file_io/include/aare/NumpyFile.hpp @@ -1,9 +1,52 @@ +#pragma once #include "aare/File.hpp" #include "aare/defs.hpp" +#include +#include -template -class NumpyFile : public File -{ +using shape_t = std::vector; + +struct dtype_t { + char byteorder; + char kind; + unsigned int itemsize; + std::string to_string() { + std::stringstream sstm; + sstm << byteorder << kind << itemsize; + return sstm.str(); + } +}; +struct header_t { + dtype_t dtype; + bool fortran_order; + shape_t shape; + std::string to_string() { + std::stringstream sstm; + sstm << "dtype: " << dtype.to_string() << ", fortran_order: " << fortran_order << ' '; + + sstm << "shape: ("; + for (auto item : shape) + sstm << item << ','; + sstm << ')'; + return sstm.str(); + } +}; +template class NumpyFile : public File { + FILE *fp = nullptr; - + public: + NumpyFile(std::filesystem::path fname); + Frame *get_frame(int frame_number) override; + header_t header{}; + static constexpr std::array magic_str{'\x93', 'N', 'U', 'M', 'P', 'Y'}; + uint8_t major_ver_{}; + uint8_t minor_ver_{}; + uint32_t header_len{}; + uint8_t header_len_size{}; + const uint8_t magic_string_length{6}; + ssize_t header_size{}; + inline ssize_t pixels_per_frame() { + return std::accumulate(header.shape.begin() + 1, header.shape.end(), 1, std::multiplies()); + }; + inline ssize_t bytes_per_frame() { return header.dtype.itemsize * pixels_per_frame(); }; }; \ No newline at end of file diff --git a/file_io/include/aare/NumpyFileFactory.hpp b/file_io/include/aare/NumpyFileFactory.hpp index 3ba3903..45e55da 100644 --- a/file_io/include/aare/NumpyFileFactory.hpp +++ b/file_io/include/aare/NumpyFileFactory.hpp @@ -1,29 +1,20 @@ -#include "aare/defs.hpp" +#pragma once #include "aare/FileFactory.hpp" #include "aare/NumpyFile.hpp" -#include -#include -#include +#include "aare/defs.hpp" #include -#include -#include -#include + + template class NumpyFileFactory : public FileFactory { + private: + std::ifstream f; + void read_data(File *_file); + public: NumpyFileFactory(std::filesystem::path fpath); void parse_metadata(File *_file) override; - void open_subfiles(File *_file) override; File *load_file() override; + void parse_fname(File *){}; - uint8_t major_ver() const noexcept { return major_ver_; } - uint8_t minor_ver() const noexcept { return minor_ver_; } - - private: - static constexpr std::array magic_str{'\x93', 'N', 'U', 'M', 'P', 'Y'}; - uint8_t major_ver_{}; - uint8_t minor_ver_{}; - uint32_t header_len{}; - uint8_t header_len_size{}; - const uint8_t magic_string_length{6}; }; \ No newline at end of file diff --git a/file_io/include/aare/NumpyHelpers.hpp b/file_io/include/aare/NumpyHelpers.hpp new file mode 100644 index 0000000..21b08d4 --- /dev/null +++ b/file_io/include/aare/NumpyHelpers.hpp @@ -0,0 +1,146 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +inline std::string parse_str(const std::string &in) { + if ((in.front() == '\'') && (in.back() == '\'')) + return in.substr(1, in.length() - 2); + + throw std::runtime_error("Invalid python string."); +} +/** + Removes leading and trailing whitespaces + */ +inline std::string trim(const std::string &str) { + const std::string whitespace = " \t\n"; + auto begin = str.find_first_not_of(whitespace); + + if (begin == std::string::npos) + return ""; + + auto end = str.find_last_not_of(whitespace); + + return str.substr(begin, end - begin + 1); +} +inline std::vector parse_tuple(std::string in) { + std::vector v; + const char seperator = ','; + + in = trim(in); + + if ((in.front() == '(') && (in.back() == ')')) + in = in.substr(1, in.length() - 2); + else + throw std::runtime_error("Invalid Python tuple."); + + std::istringstream iss(in); + + for (std::string token; std::getline(iss, token, seperator);) { + v.push_back(token); + } + + return v; +} +inline bool parse_bool(const std::string &in) { + if (in == "True") + return true; + if (in == "False") + return false; + + throw std::runtime_error("Invalid python boolan."); +} + +inline std::string get_value_from_map(const std::string &mapstr) { + size_t sep_pos = mapstr.find_first_of(":"); + if (sep_pos == std::string::npos) + return ""; + + std::string tmp = mapstr.substr(sep_pos + 1); + return trim(tmp); +} +std::unordered_map parse_dict(std::string in, const std::vector &keys) { + std::unordered_map map; + if (keys.size() == 0) + return map; + + in = trim(in); + + // unwrap dictionary + if ((in.front() == '{') && (in.back() == '}')) + in = in.substr(1, in.length() - 2); + else + throw std::runtime_error("Not a Python dictionary."); + + std::vector> positions; + + for (auto const &value : keys) { + size_t pos = in.find("'" + value + "'"); + + if (pos == std::string::npos) + throw std::runtime_error("Missing '" + value + "' key."); + + std::pair position_pair{pos, value}; + positions.push_back(position_pair); + } + + // sort by position in dict + std::sort(positions.begin(), positions.end()); + + for (size_t i = 0; i < positions.size(); ++i) { + std::string raw_value; + size_t begin{positions[i].first}; + size_t end{std::string::npos}; + + std::string key = positions[i].second; + + if (i + 1 < positions.size()) + end = positions[i + 1].first; + + raw_value = in.substr(begin, end - begin); + + raw_value = trim(raw_value); + + if (raw_value.back() == ',') + raw_value.pop_back(); + + map[key] = get_value_from_map(raw_value); + } + + return map; +} + +template inline bool in_array(T val, const std::array &arr) { + return std::find(std::begin(arr), std::end(arr), val) != std::end(arr); +} +inline bool is_digits(const std::string &str) { return std::all_of(str.begin(), str.end(), ::isdigit); } + +inline dtype_t parse_descr(std::string typestring) { + if (typestring.length() < 3) { + throw std::runtime_error("invalid typestring (length)"); + } + + char byteorder_c = typestring.at(0); + char kind_c = typestring.at(1); + std::string itemsize_s = typestring.substr(2); + + if (!in_array(byteorder_c, endian_chars)) { + throw std::runtime_error("invalid typestring (byteorder)"); + } + + if (!in_array(kind_c, numtype_chars)) { + throw std::runtime_error("invalid typestring (kind)"); + } + + if (!is_digits(itemsize_s)) { + throw std::runtime_error("invalid typestring (itemsize)"); + } + unsigned int itemsize = std::stoul(itemsize_s); + + return {byteorder_c, kind_c, itemsize}; +} diff --git a/file_io/src/FileFactory.cpp b/file_io/src/FileFactory.cpp index 9e7ed47..37eaf0c 100644 --- a/file_io/src/FileFactory.cpp +++ b/file_io/src/FileFactory.cpp @@ -1,6 +1,7 @@ #include "aare/FileFactory.hpp" #include "aare/File.hpp" #include "aare/JsonFileFactory.hpp" +#include "aare/NumpyFileFactory.hpp" #include template @@ -20,7 +21,7 @@ FileFactory *FileFactory::get_factory(st // check if extension is numpy else if (fpath.extension() == ".npy") { std::cout << "Loading numpy file" << std::endl; - throw std::runtime_error("Numpy file not implemented"); + return new NumpyFileFactory(fpath); } throw std::runtime_error("Unsupported file type"); diff --git a/file_io/src/JsonFile.cpp b/file_io/src/JsonFile.cpp index c900e74..503b6fa 100644 --- a/file_io/src/JsonFile.cpp +++ b/file_io/src/JsonFile.cpp @@ -2,6 +2,9 @@ template Frame *JsonFile::get_frame(int frame_number) { + if (frame_number > this->total_frames) { + throw std::runtime_error("Frame number out of range"); + } int subfile_id = frame_number / this->max_frames_per_file; std::byte *buffer; size_t frame_size = this->subfiles[subfile_id]->bytes_per_frame(); diff --git a/file_io/src/NumpyFile.cpp b/file_io/src/NumpyFile.cpp index e69de29..245942e 100644 --- a/file_io/src/NumpyFile.cpp +++ b/file_io/src/NumpyFile.cpp @@ -0,0 +1,24 @@ + +#include "aare/NumpyFile.hpp" + +template +NumpyFile::NumpyFile(std::filesystem::path fname){ + this->fname = fname; + fp = fopen(fname.c_str(), "rb"); +} + +template +Frame *NumpyFile::get_frame(int frame_number) { + if (fp == nullptr) { + throw std::runtime_error("File not open"); + } + if (frame_number > header.shape[0]) { + throw std::runtime_error("Frame number out of range"); + } + Frame *frame = new Frame(header.shape[1], header.shape[2]); + fseek(fp, header_size + frame_number * bytes_per_frame(), SEEK_SET); + fread(frame->_get_data(), sizeof(DataType), pixels_per_frame(), fp); + return frame; +} + +template class NumpyFile; \ No newline at end of file diff --git a/file_io/src/NumpyFileFactory.cpp b/file_io/src/NumpyFileFactory.cpp index fc5d470..42e2056 100644 --- a/file_io/src/NumpyFileFactory.cpp +++ b/file_io/src/NumpyFileFactory.cpp @@ -1,173 +1,22 @@ #include "aare/NumpyFileFactory.hpp" - +#include "aare/NumpyHelpers.hpp" template NumpyFileFactory::NumpyFileFactory(std::filesystem::path fpath) { - this->fpath = fpath; + this->m_fpath = fpath; } -inline std::string parse_str(const std::string &in) { - if ((in.front() == '\'') && (in.back() == '\'')) - return in.substr(1, in.length() - 2); - - throw std::runtime_error("Invalid python string."); -} -/** - Removes leading and trailing whitespaces - */ -inline std::string trim(const std::string& str) { - const std::string whitespace = " \t"; - auto begin = str.find_first_not_of(whitespace); - - if (begin == std::string::npos) - return ""; - - auto end = str.find_last_not_of(whitespace); - - return str.substr(begin, end - begin + 1); -} -inline std::vector parse_tuple(std::string in) { - std::vector v; - const char seperator = ','; - - in = trim(in); - - if ((in.front() == '(') && (in.back() == ')')) - in = in.substr(1, in.length() - 2); - else - throw std::runtime_error("Invalid Python tuple."); - - std::istringstream iss(in); - - for (std::string token; std::getline(iss, token, seperator);) { - v.push_back(token); - } - - return v; -} -inline bool parse_bool(const std::string &in) { - if (in == "True") - return true; - if (in == "False") - return false; - - throw std::runtime_error("Invalid python boolan."); -} - - -inline std::string get_value_from_map(const std::string &mapstr) { - size_t sep_pos = mapstr.find_first_of(":"); - if (sep_pos == std::string::npos) - return ""; - - std::string tmp = mapstr.substr(sep_pos + 1); - return trim(tmp); -} -std::unordered_map parse_dict(std::string in, const std::vector &keys) { - std::unordered_map map; - - if (keys.size() == 0) - return map; - - in = trim(in); - - // unwrap dictionary - if ((in.front() == '{') && (in.back() == '}')) - in = in.substr(1, in.length() - 2); - else - throw std::runtime_error("Not a Python dictionary."); - - std::vector> positions; - - for (auto const &value : keys) { - size_t pos = in.find("'" + value + "'"); - - if (pos == std::string::npos) - throw std::runtime_error("Missing '" + value + "' key."); - - std::pair position_pair{pos, value}; - positions.push_back(position_pair); - } - - // sort by position in dict - std::sort(positions.begin(), positions.end()); - - for (size_t i = 0; i < positions.size(); ++i) { - std::string raw_value; - size_t begin{positions[i].first}; - size_t end{std::string::npos}; - - std::string key = positions[i].second; - - if (i + 1 < positions.size()) - end = positions[i + 1].first; - - raw_value = in.substr(begin, end - begin); - - raw_value = trim(raw_value); - - if (raw_value.back() == ',') - raw_value.pop_back(); - - map[key] = get_value_from_map(raw_value); - } - - return map; -} - - -using shape_t = std::vector; - -struct dtype_t { - char byteorder; - char kind; - unsigned int itemsize; -}; -struct header_t { - dtype_t dtype; - bool fortran_order; - shape_t shape; -}; -template inline bool in_array(T val, const std::array &arr) { - return std::find(std::begin(arr), std::end(arr), val) != std::end(arr); -} -inline bool is_digits(const std::string &str) { return std::all_of(str.begin(), str.end(), ::isdigit); } - -inline dtype_t parse_descr(std::string typestring) { - if (typestring.length() < 3) { - throw std::runtime_error("invalid typestring (length)"); - } - - char byteorder_c = typestring.at(0); - char kind_c = typestring.at(1); - std::string itemsize_s = typestring.substr(2); - - if (!in_array(byteorder_c, endian_chars)) { - throw std::runtime_error("invalid typestring (byteorder)"); - } - - if (!in_array(kind_c, numtype_chars)) { - throw std::runtime_error("invalid typestring (kind)"); - } - - if (!is_digits(itemsize_s)) { - throw std::runtime_error("invalid typestring (itemsize)"); - } - unsigned int itemsize = std::stoul(itemsize_s); - - return {byteorder_c, kind_c, itemsize}; -} - template void NumpyFileFactory::parse_metadata(File *_file) { auto file = dynamic_cast *>(_file); // open ifsteam to file - std::ifstream f(file->fname, std::ios::binary); + f = std::ifstream(file->fname, std::ios::binary); + // check if file exists if (!f.is_open()) { throw std::runtime_error(fmt::format("Could not open: {} for reading", file->fname.c_str())); } // read magic number std::array tmp{}; f.read(tmp.data(), tmp.size()); - if (tmp != NumpyFileFactory::magic_str) { + if (tmp != NumpyFile::magic_str) { for (auto item : tmp) fmt::print("{}, ", int(item)); fmt::print("\n"); @@ -175,29 +24,31 @@ void NumpyFileFactory::parse_metadata(File(&major_ver_), 1); - f.read(reinterpret_cast(&minor_ver_), 1); + f.read(reinterpret_cast(&file->major_ver_), 1); + f.read(reinterpret_cast(&file->minor_ver_), 1); - if (major_ver_ == 1) { - header_len_size = 2; - } else if (major_ver_ == 2) { - header_len_size = 4; + if (file->major_ver_ == 1) { + file->header_len_size = 2; + } else if (file->major_ver_ == 2) { + file->header_len_size = 4; } else { throw std::runtime_error("Unsupported numpy version"); } // read header length - f.read(reinterpret_cast(&header_len), header_len_size); - if ((magic_string_length + 2 + header_len_size + header_len) % 16 != 0) { + f.read(reinterpret_cast(&file->header_len), file->header_len_size); + file->header_size = file->magic_string_length + 2 + file->header_len_size + file->header_len; + if (file->header_size % 16 != 0) { fmt::print("Warning: header length is not a multiple of 16\n"); } // read header - auto buf_v = std::vector(header_len); - f.read(buf_v.data(), header_len); - std::string header(buf_v.data(), header_len); + auto buf_v = std::vector(file->header_len); + f.read(buf_v.data(), file->header_len); + std::string header(buf_v.data(), file->header_len); // parse header std::vector keys{"descr", "fortran_order", "shape"}; + std::cout << "original header: " << '"' << header << '"' << std::endl; auto dict_map = parse_dict(header, keys); if (dict_map.size() == 0) @@ -220,6 +71,23 @@ void NumpyFileFactory::parse_metadata(File(std::stoul(item)); shape.push_back(dim); } + file->header = {dtype, fortran_order, shape}; +} + +template + File* NumpyFileFactory::load_file() { + NumpyFile *file = new NumpyFile(this->m_fpath); + parse_metadata(file); + NumpyFile *f = dynamic_cast *>(file); + std::cout << "parsed header: " << f->header.to_string() << std::endl; + + if(sizeof(DataType) != f->header.dtype.itemsize){ + std::stringstream s; + s << "Data type size mismatch: " << sizeof(DataType) << " != " << f->header.dtype.itemsize; + throw std::runtime_error(s.str()); + } + return file; +}; +template class NumpyFileFactory; + - // {dtype, fortran_order, shape}; -}; \ No newline at end of file diff --git a/python/aare/File.py b/python/aare/File.py index da454e9..fe8a026 100644 --- a/python/aare/File.py +++ b/python/aare/File.py @@ -21,7 +21,7 @@ class File: raise FileNotFoundError(f"File not found: {path}") ext = os.path.splitext(path)[1] - if ext not in (".raw", ".json"): + if ext not in (".raw", ".json", ".npy"): raise ValueError(f"Invalid file extension: {ext}") if ext == ".json": @@ -33,6 +33,17 @@ class File: bitdepth = 16 else: bitdepth = master_data["Dynamic Range"] + elif ext == ".npy": + # TODO: find solution for this. maybe add a None detector type + detector = "Jungfrau" + with open(path, "rb") as fobj: + import numpy as np + version = np.lib.format.read_magic(fobj) + # find what function to call based on the version + func_name = 'read_array_header_' + '_'.join(str(v) for v in version) + func = getattr(np.lib.format, func_name) + header = func(fobj) + bitdepth = header[2].itemsize * 8 else: NotImplementedError("Raw file not implemented yet") diff --git a/python/example/read_frame.py b/python/example/read_frame.py index 457a4ef..2191305 100644 --- a/python/example/read_frame.py +++ b/python/example/read_frame.py @@ -1,7 +1,32 @@ +import os +from pathlib import Path from aare import File, Frame +import numpy as np if __name__ == "__main__": - file = File("/home/bb/github/aare/data/jungfrau_single_master_0.json") + + + #get env variable + root_dir = Path(os.environ.get("PROJECT_ROOT_DIR")) + + # read JSON master file + data_path = str(root_dir / "data"/"jungfrau_single_master_0.json") + + file = File(data_path) frame = file.get_frame(0) print(frame.rows, frame.cols) - print(frame.get(0,0)) \ No newline at end of file + print(frame.get(0,0)) + + # read Numpy file + + data_path = str(root_dir / "data"/"test_numpy_file.npy") + file = File(data_path) + frame = file.get_frame(0) + print(frame.rows, frame.cols) + print(frame.get(0,0)) + + arr = np.array(frame.get_array()) + print(arr) + print(arr.shape) + + print(np.array_equal(arr, np.load(data_path)[0])) \ No newline at end of file diff --git a/python/src/bindings.cpp b/python/src/bindings.cpp index 91c880b..d14b0ee 100644 --- a/python/src/bindings.cpp +++ b/python/src/bindings.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "aare/defs.hpp" @@ -27,10 +28,13 @@ PYBIND11_MODULE(_aare, m) { py::class_>(m, "_Frame16") .def(py::init()) .def("get", &Frame::get) + .def("get_array", &Frame::get_array) .def_property_readonly("rows", &Frame::rows) .def_property_readonly("cols", &Frame::cols) .def_property_readonly("bitdepth", &Frame::bitdepth); + +