Is there a way to save an image as "text" and then read it back? Indeed I'm using Qt and I'm trying to save/read a base64-encoded representation of a pixmap but nothing works as expected. Here is my current implementation sprinkled with several comments:
/*
* the culprit pixmap
*/
QPixmap pixmap = QPixmap(a_given_jpg_filepath);
/*
* pixmap to array of bytes
*/
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "jpg"); // writes pixmap into bytes in jpg format
/*
* byte array to text
* this string is saved to xml (among others) and then read back
*/
const QString &str(bytes.toBase64()); // a base64-encoded version of the pixmap
/*
* now trying to construct the pixmap (having it encoded as base64)
*/
const auto wantConversion = true;
if(wantConversion) {
const QByteArray &bytes = QByteArray::fromBase64(str.toLatin1());
QPixmap(bytes); // this pixmap is not the same as the previous one
}
What am I doing wrong? Am I on the right route to the solution? Any fix/remark is welcome.
You need to call loadFromData()
const QByteArray &bytes = QByteArray::fromBase64(str.toLatin1());
QPixmap fromBase64;
fromBase64.loadFromData(bytes);
Related
I am new at DICOM and DCMTK. I have a set of BITMAP Ultrasound data that I would like to save as a lossless DICOM file.
After loadding the data in a queue frame_queue and setting all the mandatory DICOM necessary parameters for Ultrasound Multiframe images I realise a loop to add the images in the DcmDataset as follows:
std::unique_ptr<DcmDataset> dataset;
/***
DICOM parameters A setted not added here as many parameters are set
***/
std::shared_ptr<unsigned char[]> frame;
std::shared_ptr<RGBQUAD[]> colorTable;
DJEncoderRegistration::registerCodecs();
DJ_RPLossless params; // codec parameters, we use the defaults
// this causes the lossless JPEG version of the m_dataset to be created
/**/
E_TransferSyntax xfer = DcmXfer(EXS_JPEGProcess14).getXfer();
DcmPixelSequence* sequence = new DcmPixelSequence(DcmTag(DCM_PixelData, EVR_OB));
DcmPixelData* newPixelData = new DcmPixelData(DCM_PixelData);
dataset->insert(sequence, OFTrue);
OFstatic_cast(DcmPixelData*, newPixelData)->putOriginalRepresentation(xfer, NULL, sequence);
while (frame_queue.popFrame(frame, colorTable)) // we extract a new frame from the queue
{
DcmPixelItem* newItem = new DcmPixelItem(DcmTag(DCM_Item, EVR_OB));
if (newItem != NULL)
{
sequence->insert(newItem);
/* put pixel data into the item */
std::shared_ptr<Uint8[]> newDCMFrame;
colorTableToRGB(frame, colorTable, newDCMFrame, m_frameWidth, m_frameHeight);
result = newItem->putUint8Array((Uint8*)newDCMFrame.get(), m_frameHeight * m_frameWidth * 3).good(); // this returns true in my test
frame.reset();
colorTable.reset();
}
}
dataset->chooseRepresentation(EXS_JPEGProcess14, ¶ms);
dataset->saveFile(path.c_str());
The call of dataset->chooseRepresentation(EXS_JPEGProcess14, ¶ms); raises the error:
DcmDataset: Wrong class for pixel data element, cannot change representation
A DICOM File is saved but it is empty.
Does anyone know what's causing this error?
Just a guess but you are creating the dataset in uncompressed representation. In this case, the pixel data must NOT be represented as a sequence of frames. You should try calling putUint8Array() directly on the DcmPixelData element instead of inserting sequences to it.
DCMTK will change that way of encoding to the one you want to achieve directly, when you call DcmDataset::chooseRepresentation().
For uncompressed pixel data, it is not allowed to used the "encapsulated" pixel data encoding with sequences, and I suspect that this is why DcmDataset::chooseRepresentation() complains about "not finding what was expected".
So I finaly got it to work:
std::unique_ptr<DcmDataset> dataset;
/***
DICOM parameters A setted not added here as many parameters are set
***/
std::shared_ptr<unsigned char[]> frame;
std::shared_ptr<RGBQUAD[]> colorTable;
DJEncoderRegistration::registerCodecs();
DJ_RPLossless params; // codec parameters, we use the defaults
// this causes the lossless JPEG version of the m_dataset to be created
/**/
E_TransferSyntax xfer = DcmXfer(EXS_JPEGProcess14).getXfer();
DcmPixelData* newPixelData = new DcmPixelData(DCM_PixelData);
dataset->insert(newPixelData , OFTrue);
bool result = true;
static const int imageSize = m_frameWidth * m_frameHeight * 3;
std::shared_ptr<Uint8[]> imagesBufferPtr(new Uint8[imageSize * m_nbFrames], array_deleter<Uint8>());
std::vector<std::vector<Uint8>> dataImages(m_nbFrames, std::vector<Uint8>(imageSize));
int frameId = 0;
while (this->popFrame(frame, colorTable) == cwlibdicom::frameBufferStatus::BUFFER_SUCCESS && result)
{
Uint8 *img = imagesBufferPtr.get();
img = &img[frameId];
colorTableToRGB(frame, colorTable, img, m_frameWidth, m_frameHeight);
std::memcpy(&imagesBufferPtr.get()[frameId * imageSize], img.data(), imageSize);
frameId++;
frame.reset();
colorTable.reset();
}
result = newPixelData->putUint8Array(imagesBufferPtr.get(), imageSize * m_nbFrames).good();
header->setProperty(DCM_NumberOfFrames, nb_frame);
header->setProperty(DCM_SamplesPerPixel, "3");
header->setProperty(DCM_PhotometricInterpretation, "RGB");
header->setProperty(DCM_Rows, m_imageHeight);
header->setProperty(DCM_Cols, m_imageWidth);
header->setProperty(DCM_BitsAllocated, "8");
header->setProperty(DCM_BitsStored, "8");
header->setProperty(DCM_HighBit, "7");
header->setProperty(DCM_PixelRepresentation, "0");
header->setProperty(DCM_FrameIncrementPointer, "(0018, 1065)\\(F9E4, 3317)"); // Found this in a multiframe image that already existed.
dataset->chooseRepresentation(EXS_JPEGProcess14, ¶ms);
dataset->saveFile(path.c_str(), EXS_JPEGProcess14);
I am trying to make a 3D scatter plot using QtDataVisualizationQ3DScatter and then to take a QPixmap of that and save to file.
Here is my code.
//set up the 3D scatter chart
QtDataVisualization::Q3DScatter *graph = new QtDataVisualization::Q3DScatter();
QWidget *widget = QWidget::createWindowContainer(graph);
QtDataVisualization::QScatter3DSeries *series = new QtDataVisualization::QScatter3DSeries;
//set up the data variable
QtDataVisualization::QScatterDataArray data;
//add data to the data variable
for (int i; i < nodes.size(); i = i + 1)
{
data << QVector3D(x[i], y[i], z[i]);
}
//add the data to series
series->dataProxy()->addItems(data);
//add series to graph
graph->addSeries(series);
//set the graph properties
graph->activeTheme()->setType(QtDataVisualization::Q3DTheme::ThemeArmyBlue);
graph->scene()->activeCamera()->setCameraPreset(QtDataVisualization::Q3DCamera::CameraPresetIsometricRight);
graph->seriesList().at(0)->setMesh(QtDataVisualization::QAbstract3DSeries::MeshPoint);
graph->setShadowQuality(QtDataVisualization::QAbstract3DGraph::ShadowQuality(0));
//widget->show();
const QRect rectangle(0,0,widget->width(),widget->height());
QPixmap test = widget->grab(rectangle);
test.save(QString(workingDirectory) + "/test.png", "PNG");
When I uncomment the "widget->show()", the test.png is just a white square but the widget comes up and shows the full 3DScatter. When I comment out the "widget->show()", test.png is still just a white square. I can't seem to get this to work.
When commenting widget->show(), add a widget->repaint() call to force the widget to be painted/rendered before saving it in the file.
I realise this question is quite old but for anyone who's interested:
QAbstract3DGraph has a function renderToImage. This is inherited by Q3DScatter.
You should be able to grab a qimage of your scene using:
QImage myImage = graph->renderToImage();
and then convert this to a pixmap:
QPixmap p;
p.convertFromImage(myImage);
I access a camera via USB using a library from the manufacturer. I receive the information about an image via a struct:
typedef struct
{
/*! Buffer handle which contains new data. */
BUFF_HANDLE hBuffer;
/* Pointer to the beginning of the image datas (including MetaDatas(1024 bytes) if any) */
void * pDatas;
/*! Total Buffer Payload size in bytes (including image, MetaDatas(1024 bytes) and additional bytes required by USB3 protocol */
size_t iImageSize;
/*! Width of the image (not including metadata) */
size_t iImageWidth;
/*! Height of the image */
size_t iImageHeight;
/*! Pixel Type */
tImagePixelType eImagePixelType;
/*! Line Pitch: corresponds to the number of bytes between two consecutive lines
\note if MetaDatas are not activated, Line Pitch is equal to iImageWidth*eImagePixelType
\note else MetaDatas are located immediately after the number of bytes corresponding to iImageWidth*eImagePixelType
*/
size_t iLinePitch;
/*! Buffer BlockId */
unsigned long long iBlockId;
...
} tImageInfos;
All information i have about the image come from tImageInfos struct comments so there are now additional information elsewhere. I know from the tImagePixelType that the image pixel type is 12 bit: Mono12:
/*! Image Pixel Type */
typedef enum
{
...
/*! Pixel Type 12 bit: Mono12 */
eMono12 = 3
} tImagePixelType;
My goal is to display the image on a QLabel but first i have to use the raw data (pData) and turn it into an image.
With my current approach i just get displayed stripes maybe due wrong processing of the raw data. The procedure happens in a member function of a QMainWindow class:
void TragVisMain::setImageAndShowPicture(QString message, tImageInfos imageInfos)
{
// Some message
addMessageLineToLogOutput(message);
// Create image from raw data
QImage *img = new QImage(
(uchar *) imageInfos.pDatas,
static_cast<int>(imageInfos.iImageWidth),
static_cast<int>(imageInfos.iImageHeight),
QImage::Format_Mono
);
ui->logOutput->appendPlainText(
QString("image infos: [ height: %1, width: %2, iLinePitch: %3, adress: %4 ]")
.arg(
// image height
QString::number(img->height()),
// output width
QString::number(img->width()),
// iLinePitch
QString::number(imageInfos.iLinePitch),
// address of pData
QString("0x%1").arg((quintptr)imageInfos.pDatas, QT_POINTER_SIZE * 2, 16, QChar('0'))
)
);
this->iv.setImageFromQImage(*img);
this->iv.show();
// pixel type is 3 => 12 bit: Mono12
ui->logOutput->appendPlainText(QString("tImagePixelType: ").append(QString::number(imageInfos.eImagePixelType)));
}
setImageFromQImage:
void ImageViewer::setImageFromQImage(QImage image)
{
this->ui->imageLabel->setPixmap(QPixmap::fromImage(image));
}
Here is some output I gathered from an image:
image infos: [ height: 1024, width: 1280, iLinePitch: 2560, adress: 0x00000175da5b3040 ]
tImagePixelType: 3
Do you know how to transform the image data from void pointer correctly?
Then please, enlighten me...
Here is a working solution:
cv::Mat openCvImage(1024, 1280, CV_16UC1, imageInfos.pDatas);
openCvImage.convertTo(openCvImage, CV_8UC1, 0.04);
QImage qImage = QImage(
openCvImage.data,
1280,
1024,
QImage::Format_Grayscale8
);
imageLabel.setPixmap(QPixmap::fromImage(img));
if(!imageLabel.isVisible()) {
imageLabel.show();
}
I currently have the following details about an image
int nBufSize ; //contains the buffer size
void* lpBmpBuf; //The pointer to the first byte of the bitmap buffer
How can I obtain a QPixmap from this ?
Here is what I am doing so far
QByteArray b((char*)lpBmpBuf,nBufSize);
bool t = pix.loadFromData(b, 0, Qt::AutoColor);
However t is false in this case. Any suggestions ?
Copy the bitmap buffer into a byte array as you also have the length, then:
QPixmap::loadFromData(&data, 0, Qt::AutoColor);
data is the QByteArray in my example.
Also if you know the extension/type of the file you can specify it in the 2nd argument:
loadFromData(&data, "BMP");
I have a binary file called terrain.bil which has 54478160 items, and I would like to use information contained in this file to plot a 7988x6820 pixmap. And here is my code:
QFile op("terrain.bil");
op.open(QIODevice::ReadOnly);
QByteArray rawArr = op.readAll();
const qint16 *rawData = (const qint16*) rawArr.data();
op.close();
QPixmap pixmap = QPixmap(7988,6820);
pixmap.fill(Qt::white);
QPainter pixPaint(&pixmap);
QPoint q1;
for(int i=0;i<6820;++i){
for(int j=0;j<7988;++j){
q1.setX(j);//QPixmap(#Col,#Row)
q1.setY(i);
if (rawData[i*7988+j] != -9999){
pixPaint.setPen(QColor(floor(double(rawData[i*7988+j])*0.59),floor(double(rawData[i*7988+j])*0.59),floor(double(rawData[i*7988+j])*0.59),255));
}
else{
pixPaint.setPen(Qt::green);
}
pixPaint.drawPoint(q1);
}
}
scene->addPixmap(pixmap);
I tried to plot the pixmap point by point and it takes 1 minute to finish the double for loop. Is there any way that I can plot faster with this binary file?
Thanks very much!!!
I suggest writing a tool that will draw your pixmaps and serialize them as a binary file.
You can load the file directly into a pixmap and call a function to display the pixmap.
Also research "bit blitting".
You can use QImage to get direct access to the pixel data without using QPainter object.
That way you use QImage::setPixel with qRgb macro. Look here to get more info on this. Then you can convert QImage to QPixmap and add it to your scene.