File indexing completed on 2024-12-22 03:35:45
0001 /* 0002 File : ImageFilter.cpp 0003 Project : LabPlot 0004 Description : Image I/O-filter 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2015 Stefan Gerlach <stefan.gerlach@uni.kn> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "backend/datasources/filters/ImageFilter.h" 0010 #include "backend/core/column/Column.h" 0011 #include "backend/datasources/filters/ImageFilterPrivate.h" 0012 #include "backend/lib/XmlStreamReader.h" 0013 #include "backend/lib/macros.h" 0014 #include "backend/spreadsheet/Spreadsheet.h" 0015 0016 #include <KLocalizedString> 0017 #include <QImage> 0018 0019 /*! 0020 \class ImageFilter 0021 \brief Manages the import/export of data from/to an image file. 0022 0023 \ingroup datasources 0024 */ 0025 ImageFilter::ImageFilter() 0026 : AbstractFileFilter(FileType::Image) 0027 , d(new ImageFilterPrivate(this)) { 0028 } 0029 0030 ImageFilter::~ImageFilter() = default; 0031 0032 /*! 0033 returns the list of all predefined import formats. 0034 */ 0035 QStringList ImageFilter::importFormats() { 0036 return (QStringList() << i18n("Matrix (grayscale)") << i18n("XYZ (grayscale)") << i18n("XYRGB")); 0037 } 0038 0039 /*! 0040 reads the content of the file \c fileName to the data source \c dataSource. 0041 */ 0042 void ImageFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { 0043 d->readDataFromFile(fileName, dataSource, importMode); 0044 } 0045 0046 /*! 0047 writes the content of the data source \c dataSource to the file \c fileName. 0048 */ 0049 void ImageFilter::write(const QString& fileName, AbstractDataSource* dataSource) { 0050 d->write(fileName, dataSource); 0051 // emit() 0052 } 0053 0054 /////////////////////////////////////////////////////////////////////// 0055 void ImageFilter::setImportFormat(const ImageFilter::ImportFormat f) { 0056 d->importFormat = f; 0057 } 0058 0059 ImageFilter::ImportFormat ImageFilter::importFormat() const { 0060 return d->importFormat; 0061 } 0062 0063 void ImageFilter::setStartRow(const int s) { 0064 d->startRow = s; 0065 } 0066 0067 int ImageFilter::startRow() const { 0068 return d->startRow; 0069 } 0070 0071 void ImageFilter::setEndRow(const int e) { 0072 d->endRow = e; 0073 } 0074 0075 int ImageFilter::endRow() const { 0076 return d->endRow; 0077 } 0078 0079 void ImageFilter::setStartColumn(const int s) { 0080 d->startColumn = s; 0081 } 0082 0083 int ImageFilter::startColumn() const { 0084 return d->startColumn; 0085 } 0086 0087 void ImageFilter::setEndColumn(const int e) { 0088 d->endColumn = e; 0089 } 0090 0091 int ImageFilter::endColumn() const { 0092 return d->endColumn; 0093 } 0094 0095 QString ImageFilter::fileInfoString(const QString& /*fileName*/) { 0096 DEBUG("ImageFilter::fileInfoString()"); 0097 QString info; 0098 0099 // TODO 0100 return info; 0101 } 0102 0103 // ##################################################################### 0104 // ################### Private implementation ########################## 0105 // ##################################################################### 0106 0107 ImageFilterPrivate::ImageFilterPrivate(ImageFilter* owner) 0108 : q(owner) { 0109 } 0110 0111 /*! 0112 reads the content of the file \c fileName to the data source \c dataSource. 0113 Uses the settings defined in the data source. 0114 */ 0115 void ImageFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode mode) { 0116 QImage image = QImage(fileName); 0117 if (image.isNull() || image.format() == QImage::Format_Invalid) { 0118 #ifdef QT_DEBUG 0119 qDebug() << "failed to read image" << fileName << "or invalid image format"; 0120 #endif 0121 return; 0122 } 0123 0124 int cols = image.width(); 0125 int rows = image.height(); 0126 0127 // set range of rows 0128 if (endColumn == -1) 0129 endColumn = cols; 0130 if (endRow == -1) 0131 endRow = rows; 0132 int actualCols = 0, actualRows = 0; 0133 0134 switch (importFormat) { 0135 case ImageFilter::ImportFormat::MATRIX: 0136 actualCols = endColumn - startColumn + 1; 0137 actualRows = endRow - startRow + 1; 0138 break; 0139 case ImageFilter::ImportFormat::XYZ: 0140 actualCols = 3; 0141 actualRows = (endColumn - startColumn + 1) * (endRow - startRow + 1); 0142 break; 0143 case ImageFilter::ImportFormat::XYRGB: 0144 actualCols = 5; 0145 actualRows = (endColumn - startColumn + 1) * (endRow - startRow + 1); 0146 } 0147 0148 DEBUG("image format =" << image.format()); 0149 DEBUG("image w/h =" << cols << rows); 0150 DEBUG("actual rows/cols =" << actualRows << actualCols); 0151 0152 // make sure we have enough columns in the data source. 0153 int columnOffset = 0; 0154 std::vector<void*> dataContainer; 0155 0156 // TODO: support other modes 0157 QVector<AbstractColumn::ColumnMode> columnModes; 0158 columnModes.resize(actualCols); 0159 0160 // TODO: use given names? 0161 QStringList vectorNames; 0162 0163 if (dataSource) 0164 columnOffset = dataSource->prepareImport(dataContainer, mode, actualRows, actualCols, vectorNames, columnModes); 0165 else { 0166 DEBUG("data source in image import not defined! Giving up."); 0167 return; 0168 } 0169 0170 // read data 0171 switch (importFormat) { 0172 case ImageFilter::ImportFormat::MATRIX: { 0173 for (int i = 0; i < actualRows; ++i) { 0174 for (int j = 0; j < actualCols; ++j) { 0175 double value = qGray(image.pixel(j + startColumn - 1, i + startRow - 1)); 0176 static_cast<QVector<double>*>(dataContainer[j])->operator[](i) = value; 0177 } 0178 Q_EMIT q->completed(100 * i / actualRows); 0179 } 0180 break; 0181 } 0182 case ImageFilter::ImportFormat::XYZ: { 0183 int currentRow = 0; 0184 for (int i = startRow - 1; i < endRow; ++i) { 0185 for (int j = startColumn - 1; j < endColumn; ++j) { 0186 QRgb color = image.pixel(j, i); 0187 static_cast<QVector<int>*>(dataContainer[0])->operator[](currentRow) = i + 1; 0188 static_cast<QVector<int>*>(dataContainer[1])->operator[](currentRow) = j + 1; 0189 static_cast<QVector<int>*>(dataContainer[2])->operator[](currentRow) = qGray(color); 0190 currentRow++; 0191 } 0192 Q_EMIT q->completed(100 * i / actualRows); 0193 } 0194 break; 0195 } 0196 case ImageFilter::ImportFormat::XYRGB: { 0197 int currentRow = 0; 0198 for (int i = startRow - 1; i < endRow; ++i) { 0199 for (int j = startColumn - 1; j < endColumn; ++j) { 0200 QRgb color = image.pixel(j, i); 0201 static_cast<QVector<int>*>(dataContainer[0])->operator[](currentRow) = i + 1; 0202 static_cast<QVector<int>*>(dataContainer[1])->operator[](currentRow) = j + 1; 0203 static_cast<QVector<int>*>(dataContainer[2])->operator[](currentRow) = qRed(color); 0204 static_cast<QVector<int>*>(dataContainer[3])->operator[](currentRow) = qGreen(color); 0205 static_cast<QVector<int>*>(dataContainer[4])->operator[](currentRow) = qBlue(color); 0206 currentRow++; 0207 } 0208 Q_EMIT q->completed(100 * i / actualRows); 0209 } 0210 break; 0211 } 0212 } 0213 0214 auto* spreadsheet = dynamic_cast<Spreadsheet*>(dataSource); 0215 if (spreadsheet) { 0216 QString comment = i18np("numerical data, %1 element", "numerical data, %1 elements", rows); 0217 for (int n = 0; n < actualCols; ++n) { 0218 Column* column = spreadsheet->column(columnOffset + n); 0219 column->setComment(comment); 0220 column->setUndoAware(true); 0221 if (mode == AbstractFileFilter::ImportMode::Replace) { 0222 column->setSuppressDataChangedSignal(false); 0223 column->setChanged(); 0224 } 0225 } 0226 } 0227 0228 dataSource->finalizeImport(); 0229 return; 0230 } 0231 0232 /*! 0233 writes the content of \c dataSource to the file \c fileName. 0234 */ 0235 void ImageFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) { 0236 // TODO 0237 } 0238 0239 // ############################################################################## 0240 // ################## Serialization/Deserialization ########################### 0241 // ############################################################################## 0242 0243 /*! 0244 Saves as XML. 0245 */ 0246 void ImageFilter::save(QXmlStreamWriter* writer) const { 0247 writer->writeStartElement(QStringLiteral("imageFilter")); 0248 writer->writeEndElement(); 0249 } 0250 0251 /*! 0252 Loads from XML. 0253 */ 0254 bool ImageFilter::load(XmlStreamReader*) { 0255 return true; 0256 }