Export with annotation.

This commit is contained in:
Krad
2022-10-09 09:22:05 +08:00
parent d3febde981
commit f575691a60
7 changed files with 201 additions and 32 deletions

View File

@@ -0,0 +1,65 @@
//
// Created by Krad on 2022/9/29.
//
#include "AnnotationAppender.h"
#include <QImage>
#include <QPixmap>
#include <QPainter>
void AnnotationAppender::append() {
QPixmap pic = QPixmap::fromImage(*m_Image);
QPainter painter(&pic);
QPen pen;
QFont font("Futura");
font.setPixelSize(m_FontSize);
painter.setFont(font);
pen.setColor(QColor(255, 255, 0));
painter.setPen(pen);
//TopLeft
for (int i = 0; i < m_TopLeftText.size(); ++i) {
painter.drawText(m_Margin,m_Margin+i*m_Spacing+m_FontSize*(i+1),m_TopLeftText[i]);
}
//TopRight
for (int i = 0; i < m_TopRightText.size(); ++i) {
int textLength = m_TopRightText[i].size();
painter.drawText(pic.width()-m_Margin-textLength*m_FontSize/2,
m_Margin+i*m_Spacing+m_FontSize*(i+1),m_TopRightText[i]);
}
//BottomLeft
for (int i = 0; i < m_BottomLeftText.size(); ++i) {
painter.drawText(m_Margin,
pic.height()-m_Margin-i*m_Spacing - m_FontSize * i,
m_BottomLeftText[i]);
}
//BottomRight
for (int i = 0; i < m_BottomRightText.size(); ++i) {
int textLength = m_BottomRightText[i].size();
painter.drawText(pic.width()-m_Margin-textLength*m_FontSize/2,
pic.height()-m_Margin-i*m_Spacing-m_FontSize*i,m_BottomRightText[i]);
}
*m_Image = pic.toImage();
}
void AnnotationAppender::addAnnotationText(AnnotationAppender::AnnotationPosition position, QString text) {
switch (position){
case TopLeft:
m_TopLeftText.push_back(text);
break;
case TopRight:
m_TopRightText.push_back(text);
break;
case BottomLeft:
m_BottomLeftText.push_back(text);
break;
case BottomRight:
m_BottomRightText.push_back(text);
break;
}
}
AnnotationAppender::AnnotationAppender(QImage *img) {
m_Image = img;
}

View File

@@ -0,0 +1,46 @@
//
// Created by Krad on 2022/9/29.
//
#ifndef OMEGAV_ANNOTATIONAPPENDER_H
#define OMEGAV_ANNOTATIONAPPENDER_H
#include <QString>
#include <vector>
class QImage;
class AnnotationAppender {
public:
AnnotationAppender(QImage* img);
enum AnnotationPosition{
TopLeft,
TopRight,
BottomLeft,
BottomRight
};
void setMargin(int margin){
m_Margin = margin;
}
void setSpacing(int spacing){
m_Spacing = spacing;
}
void setFontSize(int size){
m_FontSize = size;
}
void addAnnotationText(AnnotationPosition position, QString text);
void append();
private:
QImage* m_Image;
int m_Margin = 5;
int m_Spacing = 3;
int m_FontSize = 16;
std::vector<QString> m_TopLeftText;
std::vector<QString> m_TopRightText;
std::vector<QString> m_BottomLeftText;
std::vector<QString> m_BottomRightText;
};
#endif //OMEGAV_ANNOTATIONAPPENDER_H

View File

@@ -5,6 +5,7 @@
#include "IO/DICOM/DICOMPixelDataHelper.h"
#include "IO/Convert/DICOMToQImageConverter.h"
#include "AnnotationAppender.h"
namespace {
const char* FileExtention[3]={
@@ -36,23 +37,16 @@ void DicomExporter::execute(ExportOptions options)
if (exportOptions.slice >= 0){
if (exportOptions.inputData.empty()
|| exportOptions.inputData[0]->GetFileNames().empty()) {
emit exportError();
emit exportError("No image selected!");
return;
}
std::string fileName = exportOptions.inputData[0]->GetFileNames()[exportOptions.slice].first;
long frame = exportOptions.inputData[0]->GetFileNames()[exportOptions.slice].second;
DICOMToQImageConverter convert;
if (frame>=0){
convert.setFrame(frame);
QImage *img = getConvertedImage(exportOptions.inputData[0],
exportOptions.slice,exportOptions.windowLevel,exportOptions.windowWidth);
auto property = exportOptions.inputData[0];
double window[2] = { (double)exportOptions.windowWidth,(double)exportOptions.windowLevel};
if (exportOptions.cornerAnnotation != ExportOptions::Disabled){
appendAnnotation(exportOptions.inputData[0],exportOptions.slice,window,img);
}
convert.setInputDICOMFile(fileName.c_str());
convert.setWindow(exportOptions.windowLevel, exportOptions.windowWidth);
if (exportOptions.inputData[0]->GetSamplePerPixel()>1)
{
convert.setColorImage(exportOptions.inputData[0]->GetSamplePerPixel());
}
convert.save();
auto img = convert.getQImage();
bool ret = img->save(QString("%1/%2%3.%4")
.arg(exportOptions.exportDirectory,exportOptions.fileNamePrefix)
.arg(0).arg(getFileExtention()),getFileExtention());
@@ -61,7 +55,7 @@ void DicomExporter::execute(ExportOptions options)
emit exportFinished();
}
else{
emit exportError();
emit exportError("Save to disk failed!");
return;
}
}
@@ -75,21 +69,12 @@ void DicomExporter::execute(ExportOptions options)
QString savedir = QString("%1/%2").arg(exportOptions.exportDirectory,property->GetSeriesUID());
dir.mkpath(savedir);
for (int j = 0; j < property->GetFileNames().size(); ++j) {
std::string fileName = property->GetFileNames()[j].first;
long frame = property->GetFileNames()[j].second;
DICOMToQImageConverter convert;
if (frame>=0){
convert.setFrame(frame);
}
convert.setInputDICOMFile(fileName.c_str());
double * window = property->GetNthWindowLevelPreset(0);
convert.setWindow(window[1], window[0]);
if (property->GetSamplePerPixel()>1)
{
convert.setColorImage(property->GetSamplePerPixel());
QImage *img = getConvertedImage(exportOptions.inputData[i],
j,window[1],window[0]);
if (exportOptions.cornerAnnotation != ExportOptions::Disabled) {
appendAnnotation(property, j, window, img);
}
convert.save();
auto img = convert.getQImage();
bool ret = img->save(QString("%1/%2%3.%4")
.arg(savedir,exportOptions.fileNamePrefix)
.arg(j).arg(getFileExtention()),getFileExtention());
@@ -106,13 +91,79 @@ void DicomExporter::execute(ExportOptions options)
emit exportFinished();
}
else{
emit exportError();
emit exportError(QString("Total %1 processed, %2 succeed!").arg(totalCount).arg(successCount));
}
}
}
QImage *DicomExporter::getConvertedImage(ExtendMedicalImageProperties* property,int slice,int wl, int ww) {
string fileName = property->GetFileNames()[slice].first;
long frame = property->GetFileNames()[slice].second;
DICOMToQImageConverter convert;
if (frame>=0){
convert.setFrame(frame);
}
convert.setInputDICOMFile(fileName.c_str());
convert.setWindow(wl, ww);
if (property->GetSamplePerPixel() > 1)
{
convert.setColorImage(property->GetSamplePerPixel());
}
convert.save();
auto img = convert.getQImage();
return img;
}
void DicomExporter::appendAnnotation(ExtendMedicalImageProperties *property, int imageNumber, const double *window, QImage *img) {
AnnotationAppender appender(img);
appender.addAnnotationText(AnnotationAppender::TopLeft,
QString("Im: %1/%2").arg(imageNumber+1)
.arg(property->GetFileNames().size()));
appender.addAnnotationText(AnnotationAppender::TopLeft,
QString("Se:%1").arg(property->GetSeriesNumber()));
bool anonymous = exportOptions.cornerAnnotation==ExportOptions::Basic;
appender.addAnnotationText(AnnotationAppender::TopRight,
!anonymous?property->GetPatientName():"***");
appender.addAnnotationText(AnnotationAppender::TopRight,
!anonymous?property->GetPatientID():"***");
QString date = property->GetPatientBirthDate()?
QString("%1/%2/%3")
.arg(property->GetPatientBirthDateYear())
.arg(property->GetPatientBirthDateMonth())
.arg(property->GetPatientBirthDateDay()):"";
appender.addAnnotationText(AnnotationAppender::TopRight,
QString("%1 %2").arg(date,property->GetPatientSex())
);
appender.addAnnotationText(AnnotationAppender::TopRight,
!anonymous?property->GetInstitutionName():"");
appender.addAnnotationText(AnnotationAppender::TopRight,
property->GetStudyID());
appender.addAnnotationText(AnnotationAppender::TopRight,
property->GetStudyDescription());
appender.addAnnotationText(AnnotationAppender::TopRight,
property->GetSeriesDescription());
if (property->GetSamplePerPixel()==1){
appender.addAnnotationText(AnnotationAppender::BottomLeft,
QString("WL:%1 WW:%2").arg(window[1])
.arg(window[0]));
}
QString date2 = property->GetAcquisitionDate()?
QString("%1/%2/%3")
.arg(property->GetAcquisitionDateYear())
.arg(property->GetAcquisitionDateMonth())
.arg(property->GetAcquisitionDateDay()):"";
QString time = property->GetAcquisitionTime();
time = time.length()<6?"":
QString("%1:%2:%3").arg(time.left(2),
time.mid(2,2), time.mid(4,2));
appender.addAnnotationText(AnnotationAppender::BottomRight,
QString("%1 %2").arg(date2)
.arg(time));
appender.append();
}
const char * DicomExporter::getFileExtention()
{
return FileExtention[exportOptions.exportFileFormat];

View File

@@ -37,7 +37,7 @@ public:
signals:
void exportFinished();
void exportError();
void exportError(QString msg);
void exportProgress(int total, int progress);
@@ -49,6 +49,9 @@ private:
ExportOptions exportOptions;
int totalCount;//the total count of image to be exported
int exportedNumber;//the exported image index
void appendAnnotation(ExtendMedicalImageProperties *property, int imageNumber, const double *window, QImage *img);
QImage *getConvertedImage(ExtendMedicalImageProperties* property,int slice,int wl, int ww);
};
#endif // DICOMEXPORTER_H

View File

@@ -17,6 +17,7 @@ void DicomExporterThread::run()
exporter = new DicomExporter();
connect(exporter, SIGNAL(exportProgress(int, int)), this, SIGNAL(exportProgress(int, int)));
connect(exporter, SIGNAL(exportFinished()), this, SIGNAL(exportFinished()));
connect(exporter, &DicomExporter::exportError, this, &DicomExporterThread::exportError);
exporter->execute(exportOptions);

View File

@@ -15,6 +15,7 @@ public:
signals:
void exportFinished();
void exportProgress(int total, int progress);
void exportError(QString msg);
public slots:

View File

@@ -2,6 +2,7 @@
#include "ui_exportdialog.h"
#include <QDesktopServices>
#include <QMessageBox>
#include "UI/Manager/ImageViewManager.h"
#include "Deprecated/Export/dicomexporterthread.h"
@@ -146,11 +147,12 @@ void ExportDialog::onBtnExportClicked()
options.windowWidth = m_Manager->getCurrentView()->getImageViewer()->GetColorWindow();
worker = new DicomExporterThread(options);
//disconnect(worker, SIGNAL(exportProgress(int, int)), this, SLOT(onExportProgress(int, int)));
connect(worker, SIGNAL(exportProgress(int, int)), this, SLOT(onExportProgress(int, int)));
//disconnect(worker, SIGNAL(exportFinished()), this, SLOT(onExportFinished()));
connect(worker, SIGNAL(exportFinished()), this, SLOT(onExportFinished()));
connect(worker, &DicomExporterThread::exportError, [=](QString msg){
QMessageBox::warning(this,"Export error!",msg);
});
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
worker->start();