UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
usNetworkGrabberPreScan3D.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/usNetworkGrabberPreScan3D.h>
35 
36 #if defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT)
37 
38 #include <QtCore/QDataStream>
39 #include <QtCore/QEventLoop>
44  : usNetworkGrabber(parent), m_motorSettings()
45 {
46  m_grabbedImage.init(0, 0);
47 
48  // allocating buffer of size 3
49  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImagePreScan3D<unsigned char> >);
50  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImagePreScan3D<unsigned char> >);
51  m_outputBuffer.push_back(new usVolumeGrabbedInfo<usImagePreScan3D<unsigned char> >);
52 
53  m_firstFrameAvailable = false;
54  m_firstVolumeAvailable = false;
55 
56  m_recordingOn = false;
57  m_firstImageTimestamp = 0;
58 
59  m_volumeField = usNetworkGrabber::ODD_EVEN;
60 
61  connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(dataArrived()));
62 }
63 
68 
74 {
76  QDataStream in;
77  in.setDevice(m_tcpSocket);
78 #if (defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT5))
79  in.setVersion(QDataStream::Qt_5_0);
80 #elif defined(USTK_HAVE_VTK_QT4)
81  in.setVersion(QDataStream::Qt_4_8);
82 #else
83  throw(vpException(vpException::fatalError, "your Qt version is not managed in ustk"));
84 #endif
85 
86  int headerType;
87  if (m_bytesLeftToRead == 0) { // do not try to read a header if last frame was not complete
88  in >> headerType;
89  if (m_verbose)
90  std::cout << "header received, type = " << headerType << std::endl;
91  } else {
92  headerType = 0; // not a header received, but a part of a frame
93  }
94  // init confirm header received
95  if (headerType == m_confirmHeader.headerId) {
96  // read whole header
97  in >> m_confirmHeader.initOk;
99 
100  if (m_confirmHeader.initOk == 0) {
101  m_tcpSocket->close();
102  throw(vpException(vpException::fatalError, "porta initialisation error closing connection."));
103  }
104  if (m_verbose)
105  std::cout << "porta init sucess, detected probe id = " << m_confirmHeader.probeId << std::endl;
106 
107  // read all acquisition parameters received
109 
111  }
112 
113  // image header received
114  else if (headerType == m_imageHeader.headerId) {
115  // read whole header
117  quint64 timestamp;
118  in >> timestamp;
119  m_imageHeader.timeStamp = timestamp;
120 
121  if (m_imageHeader.frameCount == 0) // used to save the sequence
122  m_firstImageTimestamp = timestamp;
123 
124  in >> m_imageHeader.dataRate;
126  in >> m_imageHeader.ss;
127  in >> m_imageHeader.imageType;
134 
142  in >> m_imageHeader.motorType;
143 
144  if (m_verbose) {
145  std::cout << "frameCount = " << m_imageHeader.frameCount << std::endl;
146  std::cout << "timeStamp = " << m_imageHeader.timeStamp << std::endl;
147  std::cout << "dataRate = " << m_imageHeader.dataRate << std::endl;
148  std::cout << "dataLength = " << m_imageHeader.dataLength << std::endl;
149  std::cout << "ss = " << m_imageHeader.ss << std::endl;
150  std::cout << "imageType = " << m_imageHeader.imageType << std::endl;
151  std::cout << "frameWidth = " << m_imageHeader.frameWidth << std::endl;
152  std::cout << "frameHeight = " << m_imageHeader.frameHeight << std::endl;
153  std::cout << "pixelWidth = " << m_imageHeader.pixelWidth << std::endl;
154  std::cout << "pixelHeight = " << m_imageHeader.pixelHeight << std::endl;
155  std::cout << "transmitFrequency = " << m_imageHeader.transmitFrequency << std::endl;
156  std::cout << "samplingFrequency = " << m_imageHeader.samplingFrequency << std::endl;
157  std::cout << "transducerRadius = " << m_imageHeader.transducerRadius << std::endl;
158  std::cout << "scanLinePitch = " << m_imageHeader.scanLinePitch << std::endl;
159  std::cout << "scanLineNumber = " << m_imageHeader.scanLineNumber << std::endl;
160  std::cout << "imageDepth = " << m_imageHeader.imageDepth << std::endl;
161  std::cout << "anglePerFr = " << m_imageHeader.anglePerFr << std::endl;
162  std::cout << "framesPerVolume = " << m_imageHeader.framesPerVolume << std::endl;
163  std::cout << "motorRadius = " << m_imageHeader.motorRadius << std::endl;
164  std::cout << "motorType = " << m_imageHeader.motorType << std::endl;
165  }
166 
167  // update transducer settings with image header received
170  m_grabbedImage.setDepth(m_imageHeader.imageDepth / 1000.0);
175 
176  // update motor settings
178  m_motorSettings.setFramePitch(vpMath::rad(m_imageHeader.anglePerFr));
179 
180  if (m_imageHeader.motorType == 0)
182  else if (m_imageHeader.motorType == 1)
184  else if (m_imageHeader.motorType == 2)
186 
187  m_motorSettings.setMotorRadius(m_imageHeader.motorRadius);
188 
189  // set data info
192 
193  m_grabbedImage.setTimeStamp(m_imageHeader.timeStamp);
194 
196 
198 
199  m_bytesLeftToRead -= in.readRawData((char *)m_grabbedImage.bitmap, m_imageHeader.dataLength);
200 
201  if (m_bytesLeftToRead == 0) { // we've read all the frame in 1 packet.
203  }
204  if (m_verbose)
205  std::cout << "Bytes left to read for whole frame = " << m_bytesLeftToRead << std::endl;
206 
207  }
208 
209  // we have a part of the image still not read (arrived with next tcp packet)
210  else {
211  if (m_verbose) {
212  std::cout << "reading following part of the frame" << std::endl;
213  std::cout << "local image size = " << m_grabbedImage.getSize() << std::endl;
214  }
215  m_bytesLeftToRead -= in.readRawData((char *)m_grabbedImage.bitmap + (m_grabbedImage.getSize() - m_bytesLeftToRead),
217 
218  if (m_bytesLeftToRead == 0) { // we've read the last part of the frame.
220  }
221  }
222 }
223 
228 {
229  // At this point, CURRENT_FILLED_FRAME_POSITION_IN_VEC is going to be filled
230  if (m_firstFrameAvailable) {
231  // we test if the image settings are still the same for the new frame arrived
232  usImagePreScanSettings currentSettings =
233  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getImagePreScanSettings();
234  if (currentSettings.getAxialResolution() != m_grabbedImage.getAxialResolution() ||
235  currentSettings.getTransducerRadius() != m_grabbedImage.getTransducerRadius() ||
236  currentSettings.getScanLinePitch() != m_grabbedImage.getScanLinePitch() ||
237  currentSettings.getDepth() != m_grabbedImage.getDepth() ||
238  currentSettings.getScanLineNumber() !=
239  m_grabbedImage.getHeight()) { // m_grabbedImage is "turned" so the height corresponds to the scanline numer.
240  throw(vpException(vpException::badValue, "Transducer settings changed during acquisition, somethink went wrong"));
241  }
242  if (m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getMotorSettings() != m_motorSettings)
243  throw(vpException(vpException::badValue, "Motor settings changed during acquisition, somethink went wrong"));
244  } else { // init case
245  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
246  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->setScanLineNumber(m_grabbedImage.getHeight());
247  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
248  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->setScanLineNumber(m_grabbedImage.getHeight());
249  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setImagePreScanSettings(m_grabbedImage);
250  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setScanLineNumber(m_grabbedImage.getHeight());
251 
252  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
253  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
254  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setMotorSettings(m_motorSettings);
255  }
256 
257  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)
258  ->resize(m_grabbedImage.getWidth(), m_grabbedImage.getHeight(), m_motorSettings.getFrameNumber());
259 
260  // Inserting frame in volume by inverting rows and cols voxels (along x and y axis), to match ustk volume storage
261  int volumeIndex = (m_grabbedImage.getFrameCount() / m_grabbedImage.getFramesPerVolume()); // from 0
262  bool motorSweepingInZDirection = (volumeIndex % 2 != 0);
263  int framePosition = (m_grabbedImage.getFrameCount() % m_grabbedImage.getFramesPerVolume()); // from 0 to FPV-1
264 
265  // setting timestamps
266  if (!motorSweepingInZDirection) // case of backward moving motor (opposite to Z direction)
267  framePosition = m_grabbedImage.getFramesPerVolume() - framePosition - 1; // inverting frames order
268 
269  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->addTimeStamp(m_grabbedImage.getTimeStamp(), framePosition);
270  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->setVolumeCount(volumeIndex);
271 
272  for (unsigned int i = 0; i < m_grabbedImage.getHeight(); i++)
273  for (unsigned int j = 0; j < m_grabbedImage.getWidth(); j++) {
274  (*m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC))(j, i, framePosition, m_grabbedImage(i, j));
275  }
276 
277  // we reach the end of a volume
278  if (m_firstFrameAvailable &&
279  ((framePosition == 0 && !motorSweepingInZDirection) ||
280  (framePosition == (int)m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC)->getFrameNumber() - 1 &&
281  motorSweepingInZDirection))) {
282  // Now CURRENT_FILLED_FRAME_POSITION_IN_VEC has become the last frame received
283  // So we switch pointers between MOST_RECENT_FRAME_POSITION_IN_VEC and CURRENT_FILLED_FRAME_POSITION_IN_VEC
285  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC);
286  m_outputBuffer.at(CURRENT_FILLED_FRAME_POSITION_IN_VEC) = m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC);
287  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC) = savePtr;
288 
289  if (m_recordingOn) {
290  std::vector<uint64_t> timestampsToWrite;
291  for (unsigned int i = 0; i < m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getTimeStamps().size(); i++)
292  timestampsToWrite.push_back(m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getTimeStamps().at(i) -
293  m_firstImageTimestamp);
294  m_sequenceWriter.write(*m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC), timestampsToWrite);
295  }
296 
297  m_firstVolumeAvailable = true;
298  emit(newVolumeAvailable());
299  }
300 
301  m_firstFrameAvailable = true;
302 }
303 
313 {
314  // manage first volume or if user grabs too fast
315  if (!m_firstVolumeAvailable ||
316  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC)->getVolumeCount() >=
317  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount()) {
318  // we wait until the first is available
319  QEventLoop loop;
320  loop.connect(this, SIGNAL(newVolumeAvailable()), SLOT(quit()));
321  loop.exec();
322  }
323 
324  // check parity
325  bool parityControl = false;
326  if (m_volumeField == ODD) {
327  if (m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount() % 2 == 1) {
328  parityControl = true;
329  }
330  } else if (m_volumeField == EVEN) {
331  if (m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC)->getVolumeCount() % 2 == 0) {
332  parityControl = true;
333  }
334  } else {
335  parityControl = true;
336  }
337 
338  // if parity not ok, we wait next volume
339  if (!parityControl) {
340  QEventLoop loop;
341  loop.connect(this, SIGNAL(newVolumeAvailable()), SLOT(quit()));
342  loop.exec();
343  }
344 
345  // switch pointers
347  m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC) = m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC);
348  m_outputBuffer.at(MOST_RECENT_FRAME_POSITION_IN_VEC) = savePtr;
349 
350  return m_outputBuffer.at(OUTPUT_FRAME_POSITION_IN_VEC);
351 }
352 
358 {
359  m_recordingOn = true;
360  m_sequenceWriter.setSequenceDirectory(path);
361 }
362 
366 void usNetworkGrabberPreScan3D::stopRecording() { m_recordingOn = false; }
367 
373 {
374  m_volumeField = volumeField;
375 }
376 
377 #endif
quint64 getTimeStamp() const
quint32 getFrameCount() const
void setFramesPerVolume(int framesPerVolume)
int getFramesPerVolume() const
void setTimeStamp(quint64 timeStamp)
void setFrameCount(quint32 frameCount)
void resize(const unsigned int h, const unsigned int w)
Settings associated to ultrasound pre-scan images implemented in usImageRF2D, usImageRF3D,...
void setAxialResolution(const double axialResolution)
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 setVolumeField(usVolumeField volumeField)
usVolumeGrabbedInfo< usImagePreScan3D< unsigned char > > * acquire()
void activateRecording(std::string path)
usNetworkGrabberPreScan3D(usNetworkGrabber *parent=0)
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