mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
milkImage.hpp
Go to the documentation of this file.
1/** \file milkImage.hpp
2 * \brief Interface to MILK::ImageStreamIO shared memory streams
3 * \ingroup image_processing_files
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 */
7
8//***********************************************************************//
9// Copyright 2015, 2016, 2017 Jared R. Males (jaredmales@gmail.com)
10//
11// This file is part of mxlib.
12//
13// mxlib is free software: you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation, either version 3 of the License, or
16// (at your option) any later version.
17//
18// mxlib is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25//***********************************************************************//
26
27#ifndef improc_milkImage_hpp
28#define improc_milkImage_hpp
29
30#ifdef MXLIB_MILK
31
32#ifdef MXLIB_CUDA
33#define HAVE_CUDA
34#endif
35
36#include <ImageStreamIO/ImageStreamIO.h>
37
38#include "../mxlib.hpp"
39#include "eigenImage.hpp"
40
41namespace mx
42{
43namespace improc
44{
45
46template <typename typeT>
47struct ImageStructTypeCode;
48
49template <>
50struct ImageStructTypeCode<uint8_t>
51{
52 constexpr static int TypeCode = _DATATYPE_UINT8;
53};
54
55template <>
56struct ImageStructTypeCode<int8_t>
57{
58 constexpr static int TypeCode = _DATATYPE_INT8;
59};
60
61template <>
62struct ImageStructTypeCode<char>
63{
64 constexpr static int TypeCode = _DATATYPE_INT8;
65};
66
67template <>
68struct ImageStructTypeCode<uint16_t>
69{
70 constexpr static int TypeCode = _DATATYPE_UINT16;
71};
72
73template <>
74struct ImageStructTypeCode<int16_t>
75{
76 constexpr static int TypeCode = _DATATYPE_INT16;
77};
78
79template <>
80struct ImageStructTypeCode<uint32_t>
81{
82 constexpr static int TypeCode = _DATATYPE_UINT32;
83};
84
85template <>
86struct ImageStructTypeCode<int32_t>
87{
88 constexpr static int TypeCode = _DATATYPE_INT32;
89};
90
91template <>
92struct ImageStructTypeCode<uint64_t>
93{
94 constexpr static int TypeCode = _DATATYPE_UINT64;
95};
96template <>
97struct ImageStructTypeCode<int64_t>
98{
99 constexpr static int TypeCode = _DATATYPE_INT64;
100};
101template <>
102struct ImageStructTypeCode<float>
103{
104 constexpr static int TypeCode = _DATATYPE_FLOAT;
105};
106
107template <>
108struct ImageStructTypeCode<double>
109{
110 constexpr static int TypeCode = _DATATYPE_DOUBLE;
111};
112
113template <>
114struct ImageStructTypeCode<std::complex<float>>
115{
116 constexpr static int TypeCode = _DATATYPE_COMPLEX_FLOAT;
117};
118
119template <>
120struct ImageStructTypeCode<std::complex<double>>
121{
122 constexpr static int TypeCode = _DATATYPE_COMPLEX_DOUBLE;
123};
124
125/// Class to interface with an ImageStreamIO image in shared memory
126/**
127 *
128 * Use with Eigen::Map (aliased as mx::improc::eigenMap)
129 * \code
130 * using namespace mx::improc;
131 * milkImage<float> mim("image"); //connects to image.im.shm
132 * eigenMap<float> im(mim); //the conversion operator passes a reference to the internal map
133 * im.setRandom(); // im is now a map pointed at the image data. Eigen functions are now available.
134 * im(64,64) *= 2000;
135 * im /= 0.2;
136 * \endcode
137 * Once you have changed something via the Eigen::Map you want to notify others connected to the stream
138 * via
139 * \code
140 * mim.post();
141 * \endcode
142 *
143 * \ingroup eigen_image_processing
144 */
145template <typename _dataT>
146class milkImage
147{
148 public:
149 typedef _dataT dataT; ///< The data type
150
151 protected:
152 std::string m_name; ///< The image name, from name.im.shm (the .im.shm should not be given).
153
154 IMAGE *m_image{ nullptr }; ///< Pointer to the ImageStreamIO IMAGE structure.
155
156 dataT *m_raw{ nullptr }; // The raw pointer address is stored here for checks
157
158 eigenMap<dataT> *m_map{ nullptr }; // An Eigen::Map of the array
159
160 uint64_t m_size_0{ 0 }; ///< The size[0] of the image when last opened.
161
162 uint64_t m_size_1{ 0 }; ///< The size[1] of the image when last opened.
163
164 bool m_passive{ false }; /**< If true then the cnt0 counter is not incremented on post. Usage: this will not
165 trigger the dmcomb process in cacao, so the DM will not pick up the shape until
166 something does trigger it. */
167
168 public:
169 /// Default c'tor
170 milkImage();
171
172 /// Constructor which opens the specified image, which must already exist in shared memory.
173 milkImage( const std::string &imname /**< [in] The image name, from name.im.shm
174 (the .im.shm should not be given).*/
175 );
176
177 /// Constructor which (re-)creates the specified image with the given size
178 milkImage( const std::string &imname, ///< [in] The image name, from name.im.shm (the .im.shm should not be given).
179 uint32_t sz0, ///< [in] the x size of the image
180 uint32_t sz1 ///< [in] the y size of the image
181 );
182
183 /// Constructor which (re-)creates the specified image and copies the provided image to it
184 milkImage( const std::string &imname, ///< [in] The image name, from name.im.shm (the .im.shm should not be given).
185 const eigenImage<dataT> &im ///< [in] An existing eigenImage
186 );
187
188 /// D'tor
189 ~milkImage();
190
191 /// Open and connect to an image, allocating the eigenMap.
192 /**
193 * \throws std::invalid_argument if the image type_code does not match dataT.
194 */
195 void open( const std::string &imname /**< [in] The image name, from name.im.shm
196 (the .im.shm should not be given).*/
197 );
198
199 /// Create (or re-create) and connect to an image, allocating the eigenMap.
200 /**
201 * \throws std::invalid_argument if the image type_code does not match dataT.
202 */
203 void create( const std::string &imname, ///< [in] The image name, for name.im.shm (the .im.shm should not be given).
204 uint32_t sz0, ///< [in] the x size of the image
205 uint32_t sz1 ///< [in] the y size of the image
206 );
207
208 /// Create and connect to an image using an existing eigenImage, allocating the eigenMap and copying the image to
209 /// the shmim.
210 /**
211 * \throws std::invalid_argument if the image type_code does not match dataT.
212 */
213 void create( const std::string &imname, ///< [in] The image name, for name.im.shm (the .im.shm should not be given).
214 const eigenImage<dataT> &im ///< [in] An existing eigenImage
215 );
216
217 /// Get the raw pointer
218 dataT *data();
219
220 /// Get the raw pointer
221 const dataT *data() const;
222
223 /// Get the width of the image
224 /**
225 * \returns the current value of m_size_0
226 */
227 uint32_t rows() const;
228
229 /// Get the height of the image
230 /**
231 * \returns the current value of m_size_1
232 */
233 uint32_t cols() const;
234
235 /// Get the total size of the image
236 /**
237 * \returns the current value of m_size_0 x m_size_1
238 */
239 uint64_t size() const;
240
241 /// Get the size of a dimension of the image
242 /**
243 * \returns the current value of m_size_0 or m_size_1 depending on n
244 */
245 uint32_t size( unsigned n /**< [in] the dimension to get the size of*/ ) const;
246
247 /// Set the passive flag
248 /** Sets \ref m_passive
249 *
250 */
251 void passive( bool pass /**< [in] new value of the \ref m_passive flag */ );
252
253 /// Get the passive flag
254 /** Gets \ref m_passive
255 *
256 */
257 bool passive() const;
258
259 /// Checks if the image is connected and is still the same format as when connected.
260 /** Checks on pointer value, size[], and data_type.
261 *
262 * \todo check inode!
263 *
264 * \returns true if connected and no changes
265 * \returns false if not connected or something changed. All maps are now invalid.
266 */
267 bool valid() const;
268
269 /// Reopens the image.
270 /** Same as
271 * \code
272 * close();
273 * open(m_name);
274 * \endcode
275 */
276 void reopen();
277
278 // Close the image
279 void close();
280
281 /// Get an eigenMap
282 /** Use with caution:
283 * - there is no way to know if the image has changed
284 * - you should check \ref valid() before using
285 *
286 *
287 * \returns an Eigen::Map<Array,-1,-1> reference
288 *
289 * \throws if the image is not opened
290 *
291 */
292 eigenMap<dataT> &operator()();
293
294 /// Get an eigenMap (const version)
295 /** Use with caution:
296 * - there is no way to know if the image has changed
297 * - you should check \ref valid() before using
298 *
299 *
300 * \returns an Eigen::Map<Array,-1,-1> reference
301 *
302 * \throws if the image is not opened
303 *
304 */
305 const eigenMap<dataT> &operator()() const;
306
307 /// Conversion operator returns an eigenMap
308 /** Use this like
309 * \code
310 * milkImage<float> mim("imname");
311 * eigenMap<float> im(mim); //you now have an Eigen interface to the image
312 * \endcode
313 * but with caution:
314 * - there is no way to know if the image has changed
315 * - you should check \ref valid() before using
316 *
317 *
318 * \returns an Eigen::Map<Array,-1,-1> object
319 *
320 * \throws if the image is not opened
321 *
322 */
323 operator eigenMap<dataT>();
324
325 /// Copy data from another milkImage to this shared memory stream
326 /** Sets the write flag, copies using the Eigen assignment to map, unsets the write flag, then posts.
327 *
328 * \throws on an error
329 *
330 */
331 milkImage &operator=( const milkImage &im /**< [in] the milkImage to copy to this stream*/ );
332
333 /// Copy data from an Eigen Array type to the shared memory stream
334 /** Sets the write flag, copies using the Eigen assigment to map, unsets the write flag, then posts.
335 *
336 * \throws on an error
337 *
338 */
339 template <typename eigenT>
340 milkImage &operator=( const eigenT &im /**< [in] the eigen array to copy to the stream*/ );
341
342 /// Access a single value in the array by its linear index
343 /** This does not set the write flag or post after finishing. You must manage these steps
344 * if using this to modify the array.
345 *
346 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
347 * connected and valid and that \p idx is in range.
348 *
349 * \returns a reference to the value at the given position
350 */
351 dataT &operator[]( size_t idx /**< [in] the linear index of the pixel in the array */ );
352
353 /// Access a single value in the array by its linear index (const version)
354 /** This does not set the write flag or post after finishing. You must manage these steps
355 * if using this to modify the array.
356 *
357 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
358 * connected and valid and that \p idx is in range.
359 *
360 * \returns a reference to the value at the given position
361 */
362 const dataT &operator[]( size_t idx /**< [in] the linear index of the pixel in the array */ ) const;
363
364 /// Access a single value in the array by its row and column coordinates
365 /** This does not set the write flag or post after finishing. You must manage these steps
366 * if using this to modify the array.
367 *
368 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
369 * connected and valid and that \p row and \p col are in range.
370 *
371 * \returns a reference to the value at the given position
372 */
373 dataT &operator()( int row, /**< [in] the row of the element*/
374 int col /**< [in] the columnof the element */
375 );
376
377 /// Access a single value in the array by its row and column coordinates (const version)
378 /** This does not set the write flag or post after finishing. You must manage these steps
379 * if using this to modify the array.
380 *
381 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
382 * connected and valid and that \p row and \p col are in range.
383 *
384 * \returns a reference to the value at the given position
385 */
386 const dataT &operator()( int row, /**< [in] the row of the element*/
387 int col /**< [in] the columnof the element */
388 ) const;
389
390 /// Set the write flag
391 /** The write flag is set to indicate whether or not the the data is being changed.
392 * The write flag will be set to false by \ref post().
393 *
394 * \throws if the image is not opened
395 */
396 void setWrite( bool wrflag = true /**< [in] [optional] the desired value of the write flag. Default is true.*/ );
397
398 /// Update the metadata and post all semaphores
399 /**
400 * \todo need to set wtime, have a version with atime
401 *
402 * \throws if the image is not opened
403 */
404 void post();
405
406 /// Get the value of cnt0 (the frame counter)
407 /**
408 * \returns the current value of cnt0
409 */
410 uint64_t cnt0();
411};
412
413template <typename dataT>
414milkImage<dataT>::milkImage()
415{
416}
417
418template <typename dataT>
419milkImage<dataT>::milkImage( const std::string &imname )
420{
421 open( imname );
422}
423
424template <typename dataT>
425milkImage<dataT>::milkImage( const std::string &imname, const eigenImage<dataT> &im )
426{
427 create( imname, im );
428}
429
430template <typename dataT>
431milkImage<dataT>::milkImage( const std::string &imname, uint32_t sz0, uint32_t sz1 )
432{
433 create( imname, sz0, sz1 );
434}
435
436template <typename dataT>
437milkImage<dataT>::~milkImage()
438{
439 close();
440}
441
442template <typename dataT>
443void milkImage<dataT>::open( const std::string &imname )
444{
445 if( m_image )
446 {
447 ImageStreamIO_closeIm( m_image );
448 delete m_image;
449 m_image = nullptr;
450 }
451
452 if( m_map )
453 {
454 delete m_map;
455 m_map = nullptr;
456 }
457
458 m_image = new IMAGE;
459
460 errno_t rv = ImageStreamIO_openIm( m_image, imname.c_str() );
461
462 if( rv != IMAGESTREAMIO_SUCCESS )
463 {
464 delete m_image;
465 m_image = nullptr;
466 throw( mx::exception( error_t::liberr, "ImageStreamIO_openIm returned an error" ) );
467 }
468
469 if( ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
470 {
471 delete m_image;
472 m_image = nullptr;
473 throw( mx::exception( error_t::invalidarg, "shmim datatype does not match template type" ) );
474 }
475
476 m_name = imname;
477 m_raw = (dataT *)m_image->array.raw;
478 m_size_0 = m_image->md->size[0];
479 m_size_1 = m_image->md->size[1];
480
481 m_map = new eigenMap<dataT>( m_raw, m_size_0, m_size_1 );
482}
483
484template <typename dataT>
485void milkImage<dataT>::create( const std::string &imname, uint32_t sz0, uint32_t sz1 )
486{
487 if( m_image )
488 {
489 ImageStreamIO_closeIm( m_image );
490 delete m_image;
491 m_image = nullptr;
492 }
493
494 if( m_map )
495 {
496 delete m_map;
497 m_map = nullptr;
498 }
499
500 m_image = new IMAGE;
501
502 uint32_t imsize[3];
503 imsize[0] = sz0;
504 imsize[1] = sz1;
505 imsize[2] = 1;
506
507 errno_t rv = ImageStreamIO_createIm_gpu( m_image,
508 imname.c_str(),
509 3,
510 imsize,
511 ImageStructTypeCode<dataT>::TypeCode,
512 -1,
513 1,
514 IMAGE_NB_SEMAPHORE,
515 0,
516 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
517 0 );
518
519 if( rv != IMAGESTREAMIO_SUCCESS )
520 {
521 delete m_image;
522 m_image = nullptr;
523 throw( mx::exception( error_t::liberr, "ImageStreamIO_createIm_gpu returned an error" ) );
524 }
525
526 if( ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
527 {
528 delete m_image;
529 m_image = nullptr;
530 throw( mx::exception( error_t::invalidarg, "shmim datatype does not match template type" ) );
531 }
532
533 m_name = imname;
534 m_raw = (dataT *)m_image->array.raw;
535 m_size_0 = m_image->md->size[0];
536 m_size_1 = m_image->md->size[1];
537
538 m_map = new eigenMap<dataT>( m_raw, m_size_0, m_size_1 );
539}
540
541template <typename dataT>
542void milkImage<dataT>::create( const std::string &imname, const eigenImage<dataT> &im )
543{
544 create( imname, im.rows(), im.cols() );
545 operator=( im );
546}
547
548template <typename dataT>
549eigenMap<dataT> &milkImage<dataT>::operator()()
550{
551 if( m_map == nullptr )
552 {
553 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
554 }
555
556 return *m_map;
557}
558
559template <typename dataT>
560const eigenMap<dataT> &milkImage<dataT>::operator()() const
561{
562 if( m_map == nullptr )
563 {
564 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
565 }
566
567 return *m_map;
568}
569
570template <typename dataT>
571milkImage<dataT>::operator eigenMap<dataT>()
572{
573 return operator()();
574}
575
576template <typename dataT>
577void milkImage<dataT>::reopen()
578{
579 close();
580 open( m_name );
581}
582
583template <typename dataT>
584void milkImage<dataT>::close()
585{
586 if( m_map != nullptr )
587 {
588 delete m_map;
589 m_map = nullptr;
590 }
591
592 if( m_image == nullptr )
593 {
594 return;
595 }
596
597 errno_t rv = ImageStreamIO_closeIm( m_image );
598 delete m_image;
599 m_image = nullptr;
600
601 if( rv != IMAGESTREAMIO_SUCCESS )
602 {
603 throw( mx::exception( error_t::liberr, "ImageStreamIO_closeIm returned an error" ) );
604 }
605}
606
607template <typename dataT>
608dataT *milkImage<dataT>::data()
609{
610 return m_raw;
611}
612
613template <typename dataT>
614const dataT *milkImage<dataT>::data() const
615{
616 return m_raw;
617}
618
619template <typename dataT>
620uint32_t milkImage<dataT>::rows() const
621{
622 return m_size_0;
623}
624
625template <typename dataT>
626uint32_t milkImage<dataT>::cols() const
627{
628 return m_size_1;
629}
630
631template <typename dataT>
632uint32_t milkImage<dataT>::size( unsigned n ) const
633{
634 if( n == 0 )
635 {
636 return m_size_0;
637 }
638 else if( n == 1 )
639 {
640 return m_size_1;
641 }
642
643 return 0;
644}
645
646template <typename dataT>
647uint64_t milkImage<dataT>::size() const
648{
649 return m_size_0 * m_size_1;
650}
651
652template <typename dataT>
653void milkImage<dataT>::passive( bool pass )
654{
655 m_passive = pass;
656}
657
658template <typename dataT>
659bool milkImage<dataT>::passive() const
660{
661 return m_passive;
662}
663
664template <typename dataT>
665bool milkImage<dataT>::valid() const
666{
667 if( m_image == nullptr )
668 {
669 return false;
670 }
671
672 if( m_raw != (dataT *)m_image->array.raw || m_size_0 != m_image->md->size[0] || m_size_1 != m_image->md->size[1] ||
673 ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
674 {
675 return false;
676 }
677
678 return true;
679}
680
681template <typename dataT>
682milkImage<dataT> &milkImage<dataT>::operator=( const milkImage<dataT> &im )
683{
684 if( m_image == nullptr )
685 {
686 throw( mx::exception( error_t::invalidconfig, "Image is not open (image is null)" ) );
687 }
688
689 if( m_map == nullptr )
690 {
691 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
692 }
693
694 setWrite( true );
695
696 *m_map = im(); // gets the map from im, will throw on invalid
697
698 setWrite( false );
699
700 post();
701
702 return *this;
703}
704
705template <typename dataT>
706template <typename eigenT>
707milkImage<dataT> &milkImage<dataT>::operator=( const eigenT &im )
708{
709 if( m_image == nullptr )
710 {
711 throw( mx::exception( error_t::invalidconfig, "Image is not open (image is null)" ) );
712 }
713
714 if( m_map == nullptr )
715 {
716 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
717 }
718
719 setWrite( true );
720
721 *m_map = im;
722
723 post();
724
725 return *this;
726}
727
728template <typename dataT>
729dataT &milkImage<dataT>::operator[]( size_t idx )
730{
731 return m_raw[idx];
732}
733
734template <typename dataT>
735const dataT &milkImage<dataT>::operator[]( size_t idx ) const
736{
737 return m_raw[idx];
738}
739
740template <typename dataT>
741dataT &milkImage<dataT>::operator()( int row, int col )
742{
743 return ( *m_map )( row, col );
744}
745
746template <typename dataT>
747const dataT &milkImage<dataT>::operator()( int row, int col ) const
748{
749 return ( *m_map )( row, col );
750}
751
752template <typename dataT>
753void milkImage<dataT>::setWrite( bool wrflag )
754{
755 if( m_image == nullptr )
756 {
757 throw( mx::exception( error_t::invalidconfig, "Image is not open" ) );
758 }
759
760 m_image->md->write = wrflag;
761}
762
763template <typename dataT>
764void milkImage<dataT>::post()
765{
766 if( m_image == nullptr )
767 {
768 throw( mx::exception( error_t::invalidconfig, "Image is not open" ) );
769 }
770
771 if( !m_passive )
772 {
773 errno_t rv = ImageStreamIO_UpdateIm( m_image );
774 m_image->md->atime = m_image->md->writetime;
775
776 if( rv != IMAGESTREAMIO_SUCCESS )
777 {
778 throw( mx::exception( error_t::liberr, "ImageStreamIO_UpdateIm returned an error" ) );
779 }
780 }
781 else
782 {
783 if( clock_gettime( CLOCK_ISIO, &m_image->md->writetime ) == -1 )
784 {
785 throw( mx::exception( errno2error_t( errno ), "clock_gettime returned an error" ) );
786 }
787 m_image->md->atime = m_image->md->writetime;
788
789 m_image->md->write = 0;
790 ImageStreamIO_sempost( m_image, -1 );
791 }
792}
793
794template <typename dataT>
795uint64_t milkImage<dataT>::cnt0()
796{
797 return m_image->md->cnt0;
798}
799
800} // namespace improc
801} // namespace mx
802
803#endif // MXLIB_MILK
804#endif // improc_milkImage_hpp
Augments an exception with the source file and line.
Definition exception.hpp:42
Tools for using the eigen library for image processing.
static constexpr error_t errno2error_t(const int &err)
Convert an errno code to error_t.
Definition error_t.hpp:2006
@ invalidconfig
A config setting was invalid.
@ invalidarg
An argument was invalid.
@ liberr
An error was returned by a library.
The mxlib c++ namespace.
Definition mxlib.hpp:37