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 }