UsTK : Ultrasound ToolKit  version 2.0.1 under development (2023-12-07)
Tutorial: UsTK elastography

Introduction

This tutorial expains how to perform elastography task on RF image coming through the network (with virtual server in this tutorial).

How does elastography works ?

The elastography computation requires 2 RF input images. The process estimates the axial displacements of the RF samples per blocks in the image, and produces the strain map of the tissues based on those displacements.

Running the tutorial

To run the tutorial, you have to run first the virtual server application to send a RF palpation sequence contained in ustk-dataset repository (https://github.com/lagadic/ustk-dataset), from ustk build directory:

$ ./apps/ustk/virtual-server/ustk-virtualServer --input /path/to/ustk-dataset/RFElasto

This application will send the RF sequence through TCP to the client. The client is the tutorial main application, here is the source code:

#include <iostream>
#include <visp3/ustk_core/usConfig.h>
#if (defined(USTK_HAVE_QT5) || defined(USTK_HAVE_VTK_QT)) && (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI)) && \
defined(USTK_HAVE_FFTW)
#include <QApplication>
#include <QStringList>
#include <QtCore/QThread>
#include <visp3/ustk_core/usRFToPreScan2DConverter.h>
#include <visp3/ustk_core/usSequenceWriter.h>
#include <visp3/ustk_elastography/usElastography.h>
#include <visp3/ustk_elastography/usImageElastography.h>
#include <visp3/ustk_grabber/usNetworkGrabberRF2D.h>
#include <visp3/gui/vpDisplayGDI.h>
#include <visp3/gui/vpDisplayX.h>
#include <visp3/io/vpImageIo.h>
int main(int argc, char **argv)
{
// QT application
QApplication app(argc, argv);
QString ip;
// by default we use the virtual server, based on a palpation dataset
if (app.arguments().contains(QString("--ip")))
ip = app.arguments().at(qApp->arguments().indexOf(QString("--ip")) + 1);
else
ip = QString("127.0.0.1");
usElastography *elastography = new usElastography;
elastography->setROI(40, 2700, 50, 500);
qtGrabber->setIPAddress(ip.toStdString());
qtGrabber->connectToServer();
// setting acquisition parameters
header.probeId = 15; // 4DC7 id = 15
header.slotId = 0; // top slot id = 0
header.imagingMode = 12; // B-mode = 0, RF = 12
// prepare image;
// prepare converter
vpImage<unsigned char> strainImage;
usImageElastography elastographyImage;
vpImage<vpRGBa> elastoToDisplay;
// Prepare display
#if defined(VISP_HAVE_X11)
vpDisplayX *displayElasto = NULL;
#elif defined(VISP_HAVE_GDI)
vpDisplayGDI *displayElasto = NULL;
#endif
bool displayInit = false;
bool captureRunning = true;
// sending acquisition parameters
qtGrabber->initAcquisition(header);
qtGrabber->runAcquisition();
writer.setSequenceFileName("./elastosequence.xml");
writer.setImageFileName(std::string("./sequencepreElasto%04d.png"));
std::cout << "waiting ultrasound initialisation..." << std::endl;
// our local grabbing loop
do {
if (qtGrabber->isFirstFrameAvailable()) {
grabbedFrame = qtGrabber->acquire();
std::cout << "MAIN THREAD received frame No : " << grabbedFrame->getFrameCount() << std::endl;
elastography->updateRF(*grabbedFrame);
strainImage = elastography->run();
std::cout << "strain image size : " << strainImage.getHeight() << ", " << strainImage.getWidth() << std::endl;
converter.convert(*grabbedFrame, preScanImage);
elastographyImage.setUltrasoundImage(preScanImage);
elastographyImage.setStrainMap(strainImage, 270, 40);
elastoToDisplay = elastographyImage.getElastoImage();
writer.saveImage(elastoToDisplay);
// init display
if (!displayInit && strainImage.getHeight() != 0 && strainImage.getWidth() != 0) {
#if defined(VISP_HAVE_X11)
displayElasto = new vpDisplayX(elastoToDisplay);
#elif defined(VISP_HAVE_GDI)
displayElasto = new vpDisplayGDI(elastoToDisplay);
#endif
displayInit = true;
}
// processing display
if (displayInit) {
vpDisplay::display(elastoToDisplay);
vpDisplay::display(strainImage);
vpDisplay::flush(elastoToDisplay);
vpDisplay::flush(strainImage);
}
}
else {
vpTime::wait(10);
}
} while (captureRunning);
if (displayInit) {
delete displayElasto;
}
return app.exec();
}
#else
int main()
{
std::cout << "You should intall Qt5 (with wigdets and network modules), FFTW and a display graphic system (GDI or "
"X11) to run this tutorial"
<< std::endl;
return 0;
}
#endif
Computation of a strain map using two sucessive RF images acquired at different compressions of the p...
vpImage< unsigned char > run()
void setROI(int tx, int ty, int tw, int th)
void updateRF(const usImageRF2D< short int > &image)
Class to store additionnal informations arriving on the network with ultrasound images grabbed,...
quint32 getFrameCount() const
Elastography image : contains a 2D B-Mode image (pre-scan or post-scan), with an overlaying colored s...
vpImage< vpRGBa > getElastoImage()
void setUltrasoundImage(const vpImage< unsigned char > &ultrasoundImage)
void setStrainMap(const vpImage< unsigned char > &strainMap, unsigned int heightPosition, unsigned int widthPosition)
Specific class to grab RF frames from the ultrasound station on the network.
usFrameGrabbedInfo< usImageRF2D< short int > > * acquire()
bool initAcquisition(const usNetworkGrabber::usInitHeaderSent &header)
void setIPAddress(const std::string &s_ip)
2D conversion from RF signal to pre-scan image
void convert(const usImageRF2D< short int > &rfImage, usImagePreScan2D< unsigned char > &preScanImage)
Writing of sequences of ultrasound images.
void setImageFileName(const std::string &imageFileName)
void saveImage(const ImageType &image, uint64_t timestamp=0)
void setSequenceFileName(const std::string &sequenceFileName)

You can see that the ROI selection is hard-coded, it is intentionnally done to perform the elastography on a region with huge diferences in tissues stiffness. The ROI is the region contained in the red rectangle in the first display. The second display shows the elastography result: hard tissues are in black, and soft ones in whithe.

You can run it once the virtual server is correctly running, from ustk build directory:

$ ./elastography/tutorial-elastography-2D