UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usNetworkGrabberRF3D.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ustk software.
4  * Copyright (C) 2016 - 2017 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * ("GPL") version 2 as published by the Free Software Foundation.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ustk with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * This software was developed at:
17  * Inria Rennes - Bretagne Atlantique
18  * Campus Universitaire de Beaulieu
19  * 35042 Rennes Cedex
20  * France
21  *
22  * If you have questions regarding the use of this file, please contact
23  * Inria at ustk@inria.fr
24  *
25  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27  *
28  * Authors:
29  * Pedro Patlan
30  * Marc Pouliquen
31  *
32  *****************************************************************************/
33 
34 #include <visp3/ustk_grabber/usNetworkGrabberRF3D.h>
35 
36 #if defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT)
37 
38 #include <QtCore/QDataStream>
39 #include <QtCore/QEventLoop>
44 {
45  m_grabbedImage.init(0, 0);
46 
47  // allocating buffer of size 3
48  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImageRF3D<short int> >);
49  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImageRF3D<short int> >);
50  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImageRF3D<short int> >);
51 
52  m_firstFrameAvailable = false;
53  m_firstVolumeAvailable = false;
54 
55  m_recordingOn = false;
56  m_firstImageTimestamp = 0;
57 
58  m_volumeField = usNetworkGrabber::ODD_EVEN;
59 
60  connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataArrived()));
61 }
62 
67 
73 {
75  QDataStream in;
76  in.setDevice(m_tcpSocket);
77 #if (defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT5))
78  in.setVersion(QDataStream::Qt_5_0);
79 #elif defined(USTK_HAVE_VTK_QT4)
80  in.setVersion(QDataStream::Qt_4_8);
81 #else
82  throw(vpException(vpException::fatalError, "your Qt version is not managed in ustk"));
83 #endif
84 
85  int headerType;
86  if (m_bytesLeftToRead == 0) { // do not try to read a header if last frame was not complete
87  in >> headerType;
88  if (m_verbose)
89  std::cout << "header received, type = " << headerType << std::endl;
90  } else {
91  headerType = 0; // not a header received, but a part of a frame
92  }
93  // init confirm header received
94  if (headerType == m_confirmHeader.headerId) {
95  // read whole header
96  in >> m_confirmHeader.initOk;
98 
99  if (m_confirmHeader.initOk == 0) {
100  m_tcpSocket->close();
101  throw(vpException(vpException::fatalError, "porta initialisation error closing connection."));
102  }
103  if (m_verbose)
104  std::cout << "porta init sucess, detected probe id = " << m_confirmHeader.probeId << std::endl;
105 
106  // read all acquisition parameters received
108 
110  }
111 
112  // image header received
113  else if (headerType == m_imageHeader.headerId) {
114  // read whole header
116  quint64 timestamp;
117  in >> timestamp;
118  m_imageHeader.timeStamp = timestamp;
119 
120  if (m_imageHeader.frameCount == 0) // used to save the sequence
121  m_firstImageTimestamp = timestamp;
122 
123  in >> m_imageHeader.dataRate;
125  in >> m_imageHeader.ss;
126  in >> m_imageHeader.imageType;
133 
141  in >> m_imageHeader.motorType;
142 
143  if (m_verbose) {
144  std::cout << "frameCount = " << m_imageHeader.frameCount << std::endl;
145  std::cout << "timeStamp = " << m_imageHeader.timeStamp << std::endl;
146  std::cout << "dataRate = " << m_imageHeader.dataRate << std::endl;
147  std::cout << "dataLength = " << m_imageHeader.dataLength << std::endl;
148  std::cout << "ss = " << m_imageHeader.ss << std::endl;
149  std::cout << "imageType = " << m_imageHeader.imageType << std::endl;
150  std::cout << "frameWidth = " << m_imageHeader.frameWidth << std::endl;
151  std::cout << "frameHeight = " << m_imageHeader.frameHeight << std::endl;
152  std::cout << "pixelWidth = " << m_imageHeader.pixelWidth << std::endl;
153  std::cout << "pixelHeight = " << m_imageHeader.pixelHeight << std::endl;
154  std::cout << "transmitFrequency = " << m_imageHeader.transmitFrequency << std::endl;
155  std::cout << "samplingFrequency = " << m_imageHeader.samplingFrequency << std::endl;
156  std::cout << "transducerRadius = " << m_imageHeader.transducerRadius << std::endl;
157  std::cout << "scanLinePitch = " << m_imageHeader.scanLinePitch << std::endl;
158  std::cout << "scanLineNumber = " << m_imageHeader.scanLineNumber << std::endl;
159  std::cout << "imageDepth = " << m_imageHeader.imageDepth << std::endl;
160  std::cout << "anglePerFr = " << m_imageHeader.anglePerFr << std::endl;
161  std::cout << "framesPerVolume = " << m_imageHeader.framesPerVolume << std::endl;
162  std::cout << "motorRadius = " << m_imageHeader.motorRadius << std::endl;
163  std::cout << "motorType = " << m_imageHeader.motorType << std::endl;
164  }
165 
166  // update transducer settings with image header received
169  m_grabbedImage.setDepth(m_imageHeader.imageDepth / 1000.0);
174 
175  // update motor settings
177  m_motorSettings.setFramePitch(vpMath::rad(m_imageHeader.anglePerFr));
178 
179  if (m_imageHeader.motorType == 0)
181  else if (m_imageHeader.motorType == 1)
183  else if (m_imageHeader.motorType == 2)
185 
186  m_motorSettings.setMotorRadius(m_imageHeader.motorRadius);
187 
188  // set data info
189  m_grabbedImage.setFrameCount(m_imageHeader.frameCount);
191 
192  // warning if timestamps are close (< 10 ms)
193  if (m_imageHeader.timeStamp - m_grabbedImage.getTimeStamp() < 10) {
194  std::cout << "WARNING : new image received with an acquisition timestamp close to previous image (<10ms)"
195  << std::endl;
196  }
197  m_grabbedImage.setTimeStamp(m_imageHeader.timeStamp);
198 
200 
202 
203  m_bytesLeftToRead -= in.readRawData((char *)m_grabbedImage.bitmap, m_imageHeader.dataLength);
204 
205  if (m_bytesLeftToRead == 0) { // we've read all the frame in 1 packet.
207  }
208  if (m_verbose)
209  std::cout << "Bytes left to read for whole frame = " << m_bytesLeftToRead << std::endl;
210 
211  }
212 
213  // we have a part of the image still not read (arrived with next tcp packet)
214  else {
215  if (m_verbose) {
216  std::cout << "reading following part of the frame" << std::endl;
217  std::cout << "local image size = " << m_grabbedImage.getNumberOfPixel() << std::endl;
218  }
220  in.readRawData((char *)m_grabbedImage.bitmap + ((m_grabbedImage.getNumberOfPixel() * 2) - m_bytesLeftToRead),
222 
223  if (m_bytesLeftToRead == 0) { // we've read the last part of the frame.
225  }
226  }
227 }
228 
233 {
234  // At this point, CURRENT_FILLED_FRAME_POSITION_IN_VEC is going to be filled
235  if (m_firstFrameAvailable) {
236  // we test if the image settings are still the same for the new frame arrived
237  usImagePreScanSettings currentSettings =
238  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getImagePreScanSettings();
239  if (currentSettings.getAxialResolution() != m_grabbedImage.getAxialResolution() ||
240  currentSettings.getTransducerRadius() != m_grabbedImage.getTransducerRadius() ||
241  currentSettings.getScanLinePitch() != m_grabbedImage.getScanLinePitch() ||
242  currentSettings.getDepth() != m_grabbedImage.getDepth() ||
243  currentSettings.getScanLineNumber() != m_grabbedImage.getScanLineNumber()) {
244  std::cout << m_grabbedImage;
245  std::cout << currentSettings;
246 
247  throw(vpException(vpException::badValue, "Transducer settings changed during acquisition, somethink went wrong"));
248  }
249  } else { // init case
250  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
251  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
252  }
253  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
254 
255  if (m_firstFrameAvailable) {
256  if (m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getMotorSettings() != m_motorSettings)
257  throw(vpException(vpException::badValue, "Motor settings changed during acquisition, somethink went wrong"));
258  } else { // init case
259  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
260  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
261  }
262  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
263 
264  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)
265  ->resize(m_grabbedImage.getHeight(), m_grabbedImage.getWidth(), m_motorSettings.getFrameNumber());
266 
267  // Inserting frame in volume
268  int volumeIndex = m_grabbedImage.getFrameCount() / m_grabbedImage.getFramesPerVolume(); // from 0
269  bool motorSweepingInZDirection = (volumeIndex % 2 != 0);
270  int framePosition = m_grabbedImage.getFrameCount() % m_grabbedImage.getFramesPerVolume(); // from 0 to FPV-1
271 
272  // setting timestamps
273  if (!motorSweepingInZDirection) // case of backward moving motor (opposite to Z direction)
274  framePosition = m_grabbedImage.getFramesPerVolume() - framePosition - 1;
275 
276  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->addTimeStamp(m_grabbedImage.getTimeStamp(), framePosition);
277  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setVolumeCount(volumeIndex);
278 
279  for (unsigned int i = 0; i < m_grabbedImage.getHeight(); i++) {
280  for (unsigned int j = 0; j < m_grabbedImage.getWidth(); j++) {
281  (*m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC))(i, j, framePosition, m_grabbedImage(i, j));
282  }
283  }
284 
285  // we reach the end of a volume
286  if (m_firstFrameAvailable &&
287  ((framePosition == 0 && !motorSweepingInZDirection) ||
288  (framePosition == (int)m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getFrameNumber() - 1 &&
289  motorSweepingInZDirection))) {
290  // Now CURRENT_FILLED_FRAME_POSITION_IN_VEC has become the last frame received
291  // So we switch pointers between MOST_RECENT_FRAME_POSITION_IN_VEC and CURRENT_FILLED_FRAME_POSITION_IN_VEC
293  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC) = m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC);
294  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC) = savePtr;
295 
296  if (m_recordingOn) {
297  std::vector<uint64_t> timestampsToWrite;
298  for (unsigned int i = 0; i < m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getTimeStamps().size(); i++)
299  timestampsToWrite.push_back(m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getTimeStamps().at(i) -
300  m_firstImageTimestamp);
301  m_sequenceWriter.write(*m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC), timestampsToWrite);
302  }
303 
304  m_firstVolumeAvailable = true;
305  emit(newVolumeAvailable());
306  }
307 
308  m_firstFrameAvailable = true;
309 }
310 
320 {
321  // manage first volume or if user grabs too fast
322  if (!m_firstVolumeAvailable ||
323  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->getVolumeCount() >=
324  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount()) {
325  // we wait until the next is available
326  QEventLoop loop;
327  loop.connect(this, SIGNAL(newVolumeAvailable()), SLOT(quit()));
328  loop.exec();
329  }
330 
331  // check parity
332  bool parityControl = false;
333  if (m_volumeField == ODD) {
334  if (m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount() % 2 == 1) {
335  parityControl = true;
336  }
337  } else if (m_volumeField == EVEN) {
338  if (m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount() % 2 == 0) {
339  parityControl = true;
340  }
341  } else {
342  parityControl = true;
343  }
344 
345  // if parity not ok, we wait next volume
346  if (!parityControl) {
347  QEventLoop loop;
348  loop.connect(this, SIGNAL(newVolumeAvailable()), SLOT(quit()));
349  loop.exec();
350  }
351 
352  // switch pointers
354  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC) = m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC);
355  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC) = savePtr;
356 
357  return m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC);
358 }
359 
365 {
366  m_recordingOn = true;
367  m_sequenceWriter.setSequenceDirectory(path);
368 }
369 
373 void usNetworkGrabberRF3D::stopRecording() { m_recordingOn = false; }
374 
379 void usNetworkGrabberRF3D::setVolumeField(usNetworkGrabber::usVolumeField volumeField) { m_volumeField = volumeField; }
380 #endif
quint64 getTimeStamp() const
quint32 getFrameCount() const
void setFramesPerVolume(int framesPerVolume)
int getFramesPerVolume() const
void setTimeStamp(quint64 timeStamp)
void setFrameCount(quint32 frameCount)
Settings associated to ultrasound pre-scan images implemented in usImageRF2D, usImageRF3D,...
void setAxialResolution(const double axialResolution)
unsigned int getNumberOfPixel() const
Definition: usImageRF2D.h:418
unsigned int getHeight() const
Definition: usImageRF2D.h:412
void resize(const unsigned int height, const unsigned int width)
Definition: usImageRF2D.h:328
unsigned int getWidth() const
Definition: usImageRF2D.h:424
void init(unsigned int height, unsigned int width)
Set the size of the image.
Definition: usImageRF2D.h:371
void setSequenceDirectory(const std::string sequenceDirectory)
void write(const usImageRF2D< short int > &image, const uint64_t timestamp)
unsigned int getFrameNumber() const
void setMotorType(const usMotorType &motorType)
void setMotorRadius(double motorRadius)
void setFrameNumber(unsigned int frameNumber)
void setFramePitch(double framePitch)
void activateRecording(std::string path)
usVolumeGrabbedInfo< usImageRF3D< short int > > * acquire()
usNetworkGrabberRF3D(usNetworkGrabber *parent=0)
void setVolumeField(usVolumeField volumeField)
Generic abstract class to manage tcp connection to grab ultrasound frames (on port 8080).
usInitHeaderConfirmation m_confirmHeader
@ CURRENT_FILLED_FRAME_POSITION_IN_VEC
void readAcquisitionParameters(QDataStream &stream)
void serverUpdateEnded(bool success)
us::usImageHeader m_imageHeader
QTcpSocket * m_tcpSocket
void setTransducerConvexity(const bool isTransducerConvex)
void setDepth(double depth)
void setScanLinePitch(const double scanLinePitch)
void setTransmitFrequency(const int transmitFrequency)
void setSamplingFrequency(const int samplingFrequency)
void setTransducerRadius(const double transducerRadius)
double getTransducerRadius() const
unsigned int getScanLineNumber() const
Class to store additionnal informations arriving on the network with ultrasound volumes grabbed,...
int motorType
Definition: us.h:120
double anglePerFr
Definition: us.h:117
int dataLength
Definition: us.h:89
int frameWidth
Definition: us.h:94
double dataRate
Definition: us.h:87
unsigned int scanLineNumber
Definition: us.h:109
int imageDepth
Definition: us.h:110
uint32_t frameCount
Definition: us.h:84
int samplingFrequency
Definition: us.h:101
int framesPerVolume
Definition: us.h:118
double pixelHeight
Definition: us.h:98
double motorRadius
Definition: us.h:119
int transmitFrequency
Definition: us.h:100
int frameHeight
Definition: us.h:95
int imageType
Definition: us.h:92
double pixelWidth
Definition: us.h:97
int headerId
Definition: us.h:81
uint64_t timeStamp
Definition: us.h:85
double transducerRadius
Definition: us.h:107
double scanLinePitch
Definition: us.h:108