ChimeraTK-DeviceAccess  03.18.00
MappedImage.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 #pragma once
4 
5 #include "OneDRegisterAccessor.h"
6 #include <type_traits>
7 
8 #include <cassert>
9 #include <cstring>
10 #include <iostream>
11 #include <typeindex>
12 #include <typeinfo>
13 #include <vector>
14 
15 namespace ChimeraTK {
16 
24  std::type_index dataTypeId;
25  uint32_t totalLength = 0; // 0: unknown/not set. length includes header.
26  explicit OpaqueStructHeader(std::type_index dataTypeId_) : dataTypeId(dataTypeId_) {}
27  };
28 
37  template<class StructHeader>
38  class MappedStruct {
39  public:
40  enum class InitData { Yes, No };
43  explicit MappedStruct(
45 
48  unsigned char* data();
50  size_t capacity() const;
52  size_t size() const { return static_cast<OpaqueStructHeader*>(header())->totalLength; }
54  StructHeader* header() { return reinterpret_cast<StructHeader*>(data()); }
56  void initData();
57 
58  protected:
59  // We keep the accessor instead of the naked pointer to simplify usage, like this the object stays valid even after
60  // memory used by accessor was swapped.
62  };
63 
64  /******************************* application to Image encoding *********************************/
65 
66  enum class ImgFormat : unsigned {
67  Unset = 0,
68  Gray8,
69  Gray16,
70  RGB24,
71  RGBA32,
72  // floating point formats for communication of intermediate results
73  FLOAT1,
74  FLOAT2,
75  FLOAT3,
76  FLOAT4,
77  DOUBLE1,
78  DOUBLE2,
79  DOUBLE3,
80  DOUBLE4
81  };
82  // values are defined for a bit field
83  enum class ImgOptions { RowMajor = 1, ColMajor = 0 };
84 
86  struct ImgHeader : public OpaqueStructHeader {
88 
89  uint32_t width = 0;
90  uint32_t height = 0;
92  int32_t x_start = 0;
93  int32_t y_start = 0;
95  float scale_x = 1;
96  float scale_y = 1;
98  uint32_t channels = 0;
99  uint32_t bytesPerPixel = 0;
101  uint32_t effBitsPerPixel = 0;
105  uint64_t frame = 0;
106  };
107 
108  class MappedImage;
112  template<typename ValType, ImgOptions OPTIONS>
113  class ImgView {
114  public:
115  ImgView(MappedImage* owner) : _mi(owner) {}
122  ValType& operator()(unsigned dx, unsigned dy, unsigned channel = 0);
123 
124  // simply define iterator access via pointers
125  using iterator = ValType*;
126  using value_type = ValType;
127  // for iteration over whole image
128  ValType* begin() { return beginRow(0); }
129  ValType* end() { return beginRow(header()->height); }
130  // these assume ROW-MAJOR ordering
131  ValType* beginRow(unsigned row) { return vec() + row * header()->width * header()->channels; }
132  ValType* endRow(unsigned row) { return beginRow(row + 1); }
133  ImgHeader* header();
134 
135  protected:
136  ValType* vec();
138  };
139 
147  class MappedImage : public MappedStruct<ImgHeader> {
148  public:
150 
153  void setShape(unsigned width, unsigned height, ImgFormat fmt);
154  size_t lengthForShape(unsigned width, unsigned height, ImgFormat fmt);
156  unsigned char* imgBody() { return data() + sizeof(ImgHeader); }
157 
160  template<typename UserType, ImgOptions OPTIONS = ImgOptions::RowMajor>
162  [[maybe_unused]] auto* h = header();
163  assert(h->channels > 0 && "call setShape() before interpretedView()!");
164  assert(h->bytesPerPixel == h->channels * sizeof(UserType) &&
165  "choose correct bytesPerPixel and channels value before conversion!");
166  assert(((unsigned)h->options & (unsigned)ImgOptions::RowMajor) ==
167  ((unsigned)OPTIONS & (unsigned)ImgOptions::RowMajor) &&
168  "inconsistent data ordering col/row major");
169  ImgView<UserType, OPTIONS> ret(this);
170  return ret;
171  }
172 
173  protected:
174  void formatsDefinition(ImgFormat fmt, unsigned& channels, unsigned& bytesPerPixel);
175  };
176 
177  /*************************** begin MappedStruct implementations ************************************************/
178 
179  template<class StructHeader>
182  : _accToData(accToData) {
183  static_assert(std::is_base_of<OpaqueStructHeader, StructHeader>::value,
184  "MappedStruct expects StructHeader to implement OpaqueStructHeader");
185  if(doInitData == InitData::Yes) {
186  initData();
187  }
188  }
189 
190  template<class StructHeader>
192  return _accToData.data();
193  }
194 
195  template<class StructHeader>
197  // reason for cast: getNElements not declared const
198  return const_cast<MappedStruct*>(this)->_accToData.getNElements();
199  }
200 
201  template<class StructHeader>
203  size_t sh = sizeof(StructHeader);
204  if(capacity() < sh) {
205  throw logic_error("buffer provided to MappedStruct is too small for correct initialization");
206  }
207  auto* p = data();
208  new(p) StructHeader;
209  header()->totalLength = sh; // minimal length, could be larger
210  memset(p + sh, 0, capacity() - sh);
211  }
212 
213  /*************************** begin MappedImage implementations ************************************************/
214 
215  inline void MappedImage::formatsDefinition(ImgFormat fmt, unsigned& channels, unsigned& bytesPerPixel) {
216  switch(fmt) {
217  case ImgFormat::Unset:
218  assert(false && "ImgFormat::Unset not allowed");
219  break;
220  case ImgFormat::Gray8:
221  channels = 1;
222  bytesPerPixel = 1;
223  break;
224  case ImgFormat::Gray16:
225  channels = 1;
226  bytesPerPixel = 2;
227  break;
228  case ImgFormat::RGB24:
229  channels = 3;
230  bytesPerPixel = 3;
231  break;
232  case ImgFormat::RGBA32:
233  channels = 4;
234  bytesPerPixel = 4;
235  break;
236  case ImgFormat::FLOAT1:
237  case ImgFormat::FLOAT2:
238  case ImgFormat::FLOAT3:
239  case ImgFormat::FLOAT4:
240  channels = unsigned(fmt) - unsigned(ImgFormat::FLOAT1) + 1;
241  bytesPerPixel = 4 * channels;
242  break;
243  case ImgFormat::DOUBLE1:
244  case ImgFormat::DOUBLE2:
245  case ImgFormat::DOUBLE3:
246  case ImgFormat::DOUBLE4:
247  channels = unsigned(fmt) - unsigned(ImgFormat::DOUBLE1) + 1;
248  bytesPerPixel = 8 * channels;
249  break;
250  }
251  }
252 
253  inline size_t MappedImage::lengthForShape(unsigned width, unsigned height, ImgFormat fmt) {
254  unsigned channels;
255  unsigned bytesPerPixel;
256  formatsDefinition(fmt, channels, bytesPerPixel);
257  return sizeof(ImgHeader) + (size_t)width * height * bytesPerPixel;
258  }
259 
260  inline void MappedImage::setShape(unsigned width, unsigned height, ImgFormat fmt) {
261  unsigned channels;
262  unsigned bytesPerPixel;
263  formatsDefinition(fmt, channels, bytesPerPixel);
264  size_t totalLen = lengthForShape(width, height, fmt);
265  if(totalLen > capacity()) {
266  throw logic_error("MappedImage: provided buffer to small for requested image shape");
267  }
268  auto* h = header();
269  // start with default values
270  new(h) ImgHeader;
271  h->image_format = fmt;
272  h->totalLength = totalLen;
273  h->width = width;
274  h->height = height;
275  h->channels = channels;
276  h->bytesPerPixel = bytesPerPixel;
277  }
278 
279  template<typename ValType, ImgOptions OPTIONS>
280  ValType& ImgView<ValType, OPTIONS>::operator()(unsigned dx, unsigned dy, unsigned channel) {
281  auto* h = header();
282  assert(dy < h->height);
283  assert(dx < h->width);
284  assert(channel < h->channels);
285  // this is the only place where row-major / column-major storage is decided
286  // note, definition of row major/column major is confusing for images.
287  // - for a matrix M(i,j) we say it is stored row-major if rows are stored without interleaving: M11, M12,...
288  // - for the same Matrix, if we write M(x,y) for pixel value at coordite (x,y) of an image, this means
289  // that pixel _columns_ are stored without interleaving
290  // So definition used here is opposite to matrix definition.
291  if constexpr((unsigned)OPTIONS & (unsigned)ImgOptions::RowMajor) {
292  return vec()[(dy * h->width + dx) * h->channels + channel];
293  }
294  else {
295  return vec()[(dy + dx * h->height) * h->channels + channel];
296  }
297  }
298 
299  template<typename ValType, ImgOptions OPTIONS>
301  return _mi->header();
302  }
303 
304  template<typename ValType, ImgOptions OPTIONS>
306  return reinterpret_cast<ValType*>(_mi->imgBody());
307  }
308 
309 } // namespace ChimeraTK
ChimeraTK::MappedStruct::InitData::Yes
@ Yes
ChimeraTK::ImgHeader::channels
uint32_t channels
gray=1, rgb=3, rgba=4
Definition: MappedImage.h:98
ChimeraTK::ImgHeader::x_start
int32_t x_start
start coordinates, in output
Definition: MappedImage.h:92
ChimeraTK::ImgHeader::scale_y
float scale_y
Definition: MappedImage.h:96
ChimeraTK::ImgHeader::options
ImgOptions options
Definition: MappedImage.h:103
ChimeraTK::ImgView::vec
ValType * vec()
Definition: MappedImage.h:305
ChimeraTK::ImgView::value_type
ValType value_type
Definition: MappedImage.h:126
ChimeraTK::MappedStruct< ImgHeader >::InitData
InitData
Definition: MappedImage.h:40
ChimeraTK::ImgFormat::FLOAT4
@ FLOAT4
ChimeraTK::ImgView::operator()
ValType & operator()(unsigned dx, unsigned dy, unsigned channel=0)
This allows to read/write image pixel values, for given coordinates.
Definition: MappedImage.h:280
ChimeraTK::ImgFormat::FLOAT1
@ FLOAT1
OneDRegisterAccessor.h
ChimeraTK::ImgView::ImgView
ImgView(MappedImage *owner)
Definition: MappedImage.h:115
ChimeraTK::MappedStruct::InitData::No
@ No
ChimeraTK::ImgView::iterator
ValType * iterator
Definition: MappedImage.h:125
ChimeraTK::ImgHeader::scale_x
float scale_x
can be used in output to provide scaled coordinates
Definition: MappedImage.h:95
ChimeraTK::ImgView::_mi
MappedImage * _mi
Definition: MappedImage.h:137
ChimeraTK::ImgView::beginRow
ValType * beginRow(unsigned row)
Definition: MappedImage.h:131
ChimeraTK::ImgFormat::DOUBLE3
@ DOUBLE3
ChimeraTK::ImgHeader::frame
uint64_t frame
frame number/counter
Definition: MappedImage.h:105
ChimeraTK::ImgView::begin
ValType * begin()
Definition: MappedImage.h:128
ChimeraTK::ImgFormat::RGB24
@ RGB24
ChimeraTK::MappedStruct::data
unsigned char * data()
returns pointer to data for header and struct content.
Definition: MappedImage.h:191
ChimeraTK::MappedStruct::capacity
size_t capacity() const
capacity of used container
Definition: MappedImage.h:196
ChimeraTK::ImgFormat::DOUBLE4
@ DOUBLE4
ChimeraTK::MappedImage::setShape
void setShape(unsigned width, unsigned height, ImgFormat fmt)
needs to be called after construction.
Definition: MappedImage.h:260
ChimeraTK::ImgView::end
ValType * end()
Definition: MappedImage.h:129
ChimeraTK::ImgHeader::effBitsPerPixel
uint32_t effBitsPerPixel
effective bits per pixel
Definition: MappedImage.h:101
ChimeraTK::OpaqueStructHeader::totalLength
uint32_t totalLength
Definition: MappedImage.h:25
ChimeraTK::ImgFormat::Gray8
@ Gray8
ChimeraTK::ImgHeader::image_format
ImgFormat image_format
Definition: MappedImage.h:102
ChimeraTK::ImgOptions::ColMajor
@ ColMajor
ChimeraTK::MappedImage::lengthForShape
size_t lengthForShape(unsigned width, unsigned height, ImgFormat fmt)
Definition: MappedImage.h:253
ChimeraTK::ImgFormat::FLOAT2
@ FLOAT2
ChimeraTK::OneDRegisterAccessor< unsigned char >
ChimeraTK::MappedStruct
Provides interface to a struct that is mapped onto a 1D array of ValType StructHeader must be derived...
Definition: MappedImage.h:38
ChimeraTK::MappedStruct::header
StructHeader * header()
returns header, e.g. for setting meta data
Definition: MappedImage.h:54
ChimeraTK::OpaqueStructHeader
generic header for opaque struct handling It has fields needed for communication in the same process,...
Definition: MappedImage.h:23
ChimeraTK::ImgHeader::ImgHeader
ImgHeader()
Definition: MappedImage.h:87
ChimeraTK::ImgFormat::RGBA32
@ RGBA32
ChimeraTK::ImgHeader
Image header.
Definition: MappedImage.h:86
ChimeraTK::ImgHeader::height
uint32_t height
Definition: MappedImage.h:90
ChimeraTK::ImgHeader::bytesPerPixel
uint32_t bytesPerPixel
Definition: MappedImage.h:99
ChimeraTK::ImgFormat::FLOAT3
@ FLOAT3
ChimeraTK::ImgView::header
ImgHeader * header()
Definition: MappedImage.h:300
ChimeraTK::ImgFormat::Gray16
@ Gray16
ChimeraTK::ImgOptions
ImgOptions
Definition: MappedImage.h:83
ChimeraTK::ImgFormat::DOUBLE1
@ DOUBLE1
ChimeraTK::OpaqueStructHeader::OpaqueStructHeader
OpaqueStructHeader(std::type_index dataTypeId_)
Definition: MappedImage.h:26
ChimeraTK::ImgFormat
ImgFormat
Definition: MappedImage.h:66
ChimeraTK::MappedImage::formatsDefinition
void formatsDefinition(ImgFormat fmt, unsigned &channels, unsigned &bytesPerPixel)
Definition: MappedImage.h:215
ChimeraTK::ImgView
provides convenient matrix-like access for MappedImage
Definition: MappedImage.h:113
ChimeraTK::ImgFormat::Unset
@ Unset
ChimeraTK::ImgOptions::RowMajor
@ RowMajor
ChimeraTK::MappedStruct::initData
void initData()
default initialize header and zero out data that follows
Definition: MappedImage.h:202
ChimeraTK::ImgHeader::y_start
int32_t y_start
Definition: MappedImage.h:93
ChimeraTK::MappedStruct::size
size_t size() const
currently used size
Definition: MappedImage.h:52
ChimeraTK::MappedStruct::MappedStruct
MappedStruct(ChimeraTK::OneDRegisterAccessor< unsigned char > &accToData, InitData doInitData=InitData::No)
This keeps a reference to given OneDRegisterAccessor.
Definition: MappedImage.h:180
ChimeraTK::ImgHeader::width
uint32_t width
Definition: MappedImage.h:89
ChimeraTK::MappedImage::interpretedView
ImgView< UserType, OPTIONS > interpretedView()
returns an ImgView object which can be used like a matrix.
Definition: MappedImage.h:161
ChimeraTK::MappedImage
interface to an image that is mapped onto a 1D array of ValType
Definition: MappedImage.h:147
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::MappedStruct::_accToData
ChimeraTK::OneDRegisterAccessor< unsigned char > & _accToData
Definition: MappedImage.h:61
ChimeraTK::logic_error
Exception thrown when a logic error has occured.
Definition: Exception.h:51
ChimeraTK::OpaqueStructHeader::dataTypeId
std::type_index dataTypeId
Definition: MappedImage.h:24
ChimeraTK::ImgView::endRow
ValType * endRow(unsigned row)
Definition: MappedImage.h:132
ChimeraTK::MappedImage::imgBody
unsigned char * imgBody()
returns pointer to image payload data
Definition: MappedImage.h:156
ChimeraTK::ImgFormat::DOUBLE2
@ DOUBLE2