1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See http://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Image queue for storage helper.
33  *
34  *****************************************************************************/
35 
36 #ifndef vpImageQueue_h
37 #define vpImageQueue_h
38 
39 #include <visp3/core/vpConfig.h>
40 
41 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
42 
43 #include <string>
44 #include <queue>
45 #include <mutex>
46 #include <thread>
47 #include <condition_variable>
48 
49 #include <visp3/core/vpIoTools.h>
50 #include <visp3/core/vpDisplay.h>
51 
52 /*!
53   \class vpImageQueue
54 
55   \ingroup group_io_image
56 
57   Create a queue containing images and optional additional strings that could be useful to save additional information like the timestamp.
58 
59   This call is to use with vpImageStorageWorker.
60 
61 */
62 template <class Type>
63 class vpImageQueue {
64 public:
65   struct cancelled {
66   };
67 
68   /*!
69    * Queue (FIFO) constructor. By default the max queue size is set to 1024*8.
70    *
71    * \param[in] seqname : Generic sequence name like `"folder/I%04d.png"`. If this name contains a parent folder, it will be created.
72    * \param[in] record_mode : 0 to record a sequence of images, 1 to record single images.
73    */
vpImageQueue(const std::string & seqname,int record_mode)74   vpImageQueue(const std::string &seqname, int record_mode)
75     : m_cancelled(false), m_cond(), m_queue_image(), m_queue_data(), m_maxQueueSize(1024*8), m_mutex(),
76       m_seqname(seqname), m_recording_mode(record_mode), m_start_recording(false), m_directory_to_create(false),
77       m_recording_trigger(false)
78   {
79     m_directory = vpIoTools::getParent(seqname);
80     if (! m_directory.empty()) {
81       if (! vpIoTools::checkDirectory(m_directory)) {
82         m_directory_to_create = true;
83       }
84     }
85     m_text_record_mode = std::string("Record mode: ") + (m_recording_mode ? std::string("single") : std::string("continuous"));
86   }
87 
88   /*!
89    * Emit cancel signal.
90    */
cancel()91   void cancel() {
92     std::lock_guard<std::mutex> lock(m_mutex);
93     std::cout << "Wait to finish saving images..." << std::endl;
94     m_cancelled = true;
95     m_cond.notify_all();
96   }
97 
98   /*!
99    * Return record mode; 0 when recording a sequence of images, 1 when recording recording single imagess.
100    */
getRecordingMode()101   int getRecordingMode() const
102   {
103     return m_recording_mode;
104   }
105 
106   /*!
107    * Return recording trigger indicating if recording is started.
108    */
getRecordingTrigger()109   bool getRecordingTrigger() const
110   {
111     return m_recording_trigger;
112   }
113 
114   /*!
115    * Return generic name of the sequence of images.
116    */
getSeqName()117   std::string getSeqName() const
118   {
119     return m_seqname;
120   }
121 
122   /*!
123    * Pop the image to save from the queue (FIFO).
124    *
125    * \param[out] I : Image to record.
126    * \param[out] data : Data to record.
127    *
128    */
pop(vpImage<Type> & I,std::string & data)129   void pop(vpImage<Type> &I, std::string &data) {
130     std::unique_lock<std::mutex> lock(m_mutex);
131 
132     while (m_queue_image.empty()) {
133       if (m_cancelled) {
134         throw cancelled();
135       }
136 
137       m_cond.wait(lock);
138 
139       if (m_cancelled) {
140         throw cancelled();
141       }
142     }
143 
144     I = m_queue_image.front();
145 
146     m_queue_image.pop();
147 
148     if (! m_queue_data.empty()) {
149       data = m_queue_data.front();
150       m_queue_data.pop();
151     }
152   }
153 
154   /*!
155    * Push data to save in the queue (FIFO).
156    *
157    * \param[in] I : Image to record.
158    * \param[in] data : Data to record.
159    */
push(const vpImage<Type> & I,std::string * data)160   void push(const vpImage<Type> &I, std::string *data) {
161     std::lock_guard<std::mutex> lock(m_mutex);
162 
163     m_queue_image.push(I);
164 
165     if (data != NULL) {
166       m_queue_data.push(*data);
167     }
168 
169     //Pop extra data in the queue
170     while (m_queue_image.size() > m_maxQueueSize) {
171       m_queue_image.pop();
172     }
173 
174     if (data != NULL) {
175       while(m_queue_data.size() > m_maxQueueSize) {
176         m_queue_data.pop();
177       }
178     }
179 
180     m_cond.notify_one();
181   }
182 
183   /*!
184    * Record helper that display information in the windows associated to the image, pop current image and additional data in the queue.
185    * \param[in] I : Image to record.
186    * \param[in] data : Data to record. Set to NULL when no additional data have to be considered.
187    * \param[in] trigger_recording : External trigger to start data saving.
188    * \param[in] disable_left_click : Disable left click usage to trigger data saving.
189    * \return true when the used asked to quit using a right click in the display window.
190    */
191   bool record(const vpImage<Type> &I, std::string *data = NULL, bool trigger_recording = false, bool disable_left_click = false)
192   {
193     if (! m_seqname.empty()) {
194       if (! disable_left_click) {
195         if (! m_recording_mode) { // continuous
196           if (m_start_recording) {
197             vpDisplay::displayText(I, 20*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), "Left  click: stop recording", vpColor::red);
198           }
199           else {
200             vpDisplay::displayText(I, 20*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), "Left  click: start recording", vpColor::red);
201           }
202         }
203         else {
204           vpDisplay::displayText(I, 20*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), "Left  click: record image", vpColor::red);
205         }
206       }
207       vpDisplay::displayText(I, 40*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), "Right click: quit", vpColor::red);
208     }
209     else {
210       vpDisplay::displayText(I, 20*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), "Click to quit", vpColor::red);
211     }
212 
213     if (! m_seqname.empty()) {
214       vpDisplay::displayText(I, 60*vpDisplay::getDownScalingFactor(I), 10*vpDisplay::getDownScalingFactor(I), m_text_record_mode, vpColor::red);
215     }
216     vpMouseButton::vpMouseButtonType button;
217     if (vpDisplay::getClick(I, button, false)) {
218       if (! m_seqname.empty()) { // Recording requested
219         if (button == vpMouseButton::button1 && ! disable_left_click) { // enable/disable recording
220           m_start_recording = !m_start_recording;
221         }
222         else if (button == vpMouseButton::button3) { // quit
223           return true;
224         }
225       }
226       else { // any button to quit
227         return true;
228       }
229     }
230 
231     if (trigger_recording) {
232       m_start_recording = true;
233     }
234 
235     m_recording_trigger = m_start_recording;
236 
237     if (m_start_recording) {
238 
239       if (m_directory_to_create) {
240         std::cout << "Create directory \"" << m_directory << "\"" << std::endl;
241         vpIoTools::makeDirectory(m_directory);
242         m_directory_to_create = false;
243       }
244 
245       push(I, data);
246 
247       if (m_recording_mode == 1) { // single shot mode
248         m_start_recording = false;
249       }
250     }
251     return false;
252   }
253 
254   /*!
255    * Set queue size.
256    * \param[in] max_queue_size : Queue size.
257    */
setMaxQueueSize(const size_t max_queue_size)258   void setMaxQueueSize(const size_t max_queue_size) {
259     m_maxQueueSize = max_queue_size;
260   }
261 
262 private:
263   bool m_cancelled;
264   std::condition_variable m_cond;
265   std::queue<vpImage<Type> > m_queue_image;
266   std::queue<std::string > m_queue_data;
267   size_t m_maxQueueSize;
268   std::mutex m_mutex;
269   std::string m_seqname;
270   std::string m_directory;
271   int m_recording_mode;
272   bool m_start_recording;
273   std::string m_text_record_mode;
274   bool m_directory_to_create;
275   bool m_recording_trigger;
276 };
277 
278 #endif
279 #endif
280