File indexing completed on 2024-04-21 03:50:55

0001 /*************************************************************************
0002  *  Copyright (C) 2020 by Jean Lima Andrade <jeno.andrade@gmail.com>     *
0003  *                                                                       *
0004  *  This program is free software; you can redistribute it and/or        *
0005  *  modify it under the terms of the GNU General Public License as       *
0006  *  published by the Free Software Foundation; either version 3 of       *
0007  *  the License, or (at your option) any later version.                  *
0008  *                                                                       *
0009  *  This program is distributed in the hope that it will be useful,      *
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of       *
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
0012  *  GNU General Public License for more details.                         *
0013  *                                                                       *
0014  *  You should have received a copy of the GNU General Public License    *
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.*
0016  *************************************************************************/
0017 
0018 #include "core/markedclass.h"
0019 #include "core/serializer.h"
0020 #include "image/polygon.h"
0021 #include "text/sentence.h"
0022 #include "util/fileutils.h"
0023 
0024 #include <memory>
0025 
0026 #include <QDir>
0027 #include <QFile>
0028 #include <QHash>
0029 #include <QJsonArray>
0030 #include <QJsonDocument>
0031 #include <QJsonObject>
0032 #include <QRegularExpression>
0033 #include <QtGlobal>
0034 #include <QXmlStreamWriter>
0035 
0036 QVector<MarkedObject*> Serializer::read(const QString& filepath)
0037 {
0038     QVector<MarkedObject*> objects;
0039 
0040     if (QFile::exists(filepath)) {
0041         if (filepath.endsWith(".xml"))
0042             objects = readXML(filepath);
0043         else if (filepath.endsWith(".json"))
0044             objects = readJSON(filepath);
0045     }
0046 
0047     return objects;
0048 }
0049 
0050 QString Serializer::serialize(const QVector<MarkedObject*>& objects, OutputType outputType)
0051 {
0052     if (outputType == OutputType::XML)
0053         return toXML(objects);
0054 
0055     else if (outputType == OutputType::JSON)
0056         return toJSON(objects);
0057 
0058     return QString();
0059 }
0060 
0061 QString Serializer::toXML(const QVector<MarkedObject*>& objects)
0062 {
0063     if (objects.isEmpty())
0064         return QString();
0065 
0066     QString xmldoc;
0067 
0068     QXmlStreamWriter xmlWriter(&xmldoc);
0069     xmlWriter.setAutoFormatting(true);
0070     xmlWriter.writeStartDocument();
0071     xmlWriter.writeStartElement("annotation");
0072 
0073     auto writeXMLObject = [&](const QString& unitName, int x, int y) {
0074         xmlWriter.writeStartElement(unitName);
0075 
0076         xmlWriter.writeStartElement("x");
0077         xmlWriter.writeCharacters(QString::number(x));
0078         xmlWriter.writeEndElement();
0079 
0080         xmlWriter.writeStartElement("y");
0081         xmlWriter.writeCharacters(QString::number(y));
0082         xmlWriter.writeEndElement();
0083 
0084         xmlWriter.writeEndElement();
0085     };
0086 
0087     for (MarkedObject* object : objects) {
0088         xmlWriter.writeStartElement("object");
0089 
0090         xmlWriter.writeStartElement("class");
0091         xmlWriter.writeCharacters(object->objClass()->name());
0092         xmlWriter.writeEndElement();
0093 
0094         if (object->type() == MarkedObject::Type::Sentence) {
0095             const Sentence *sentence = static_cast<const Sentence*>(object);
0096             writeXMLObject(sentence->unitName(), sentence->begin(), sentence->end());
0097         }
0098         else if (object->type() == MarkedObject::Type::Polygon) {
0099             const Polygon *polygon = static_cast<const Polygon*>(object);
0100             xmlWriter.writeStartElement("Polygon");
0101             for (const QPointF& point : *polygon)
0102                 writeXMLObject(polygon->unitName(), point.x(), point.y());
0103             xmlWriter.writeEndElement();
0104         }
0105 
0106         xmlWriter.writeEndElement();
0107     }
0108 
0109     xmlWriter.writeEndElement();
0110 
0111     xmlWriter.writeEndElement();
0112     xmlWriter.writeEndDocument();
0113 
0114     return xmldoc;
0115 }
0116 
0117 QString Serializer::toJSON(const QVector<MarkedObject*>& objects)
0118 {
0119     if (objects.isEmpty())
0120         return QString();
0121 
0122     QJsonArray classesArray;
0123 
0124     auto createUnitObject = [](int x, int y) {
0125         QJsonObject unitObject;
0126         unitObject.insert("x", QString::number(x));
0127         unitObject.insert("y", QString::number(y));
0128         return unitObject;
0129     };
0130 
0131     for (MarkedObject* object : objects) {
0132         QJsonObject recordObject;
0133         recordObject.insert("Class", object->objClass()->name());
0134 
0135         if (object->type() == MarkedObject::Type::Sentence) {
0136             const Sentence *sentence = static_cast<const Sentence*>(object);
0137             QJsonObject unitObject = createUnitObject(sentence->begin(), sentence->end());
0138             recordObject.insert(object->unitName(), unitObject);
0139         }
0140         else if (object->type() == MarkedObject::Type::Polygon) {
0141             QJsonArray objectsArray;
0142 
0143             const Polygon *polygon = static_cast<const Polygon*>(object);
0144             for (const QPointF& point : *polygon) {
0145                 QJsonObject unitObject = createUnitObject(point.x(), point.y());
0146                 QJsonObject markedObject;
0147                 markedObject.insert(object->unitName(), unitObject);
0148                 objectsArray.push_back(markedObject);
0149             }
0150 
0151             recordObject.insert("Polygon", objectsArray);
0152         }
0153 
0154         classesArray.push_back(recordObject);
0155     }
0156 
0157     return QJsonDocument(classesArray).toJson();
0158 }
0159 
0160 QVector<MarkedObject*> Serializer::readJSON(const QString& filepath)
0161 {
0162     QVector<MarkedObject*> savedObjects;
0163     QHash<QString, MarkedClass*> markedClasses;
0164 
0165     QByteArray data = getData(filepath);
0166     QJsonDocument doc = QJsonDocument::fromJson(data);
0167 
0168     QJsonArray objectArray = doc.array();
0169 
0170     for (const QJsonValue& jsonObj : qAsConst(objectArray)) {
0171         QString className = jsonObj["Class"].toString();
0172 
0173         MarkedObject::Type objectType;
0174         if (jsonObj["st"] != QJsonValue::Undefined)
0175             objectType = MarkedObject::Type::Sentence;
0176         else if (jsonObj["Polygon"] != QJsonValue::Undefined)
0177             objectType = MarkedObject::Type::Polygon;
0178 
0179         if (!markedClasses.contains(className))
0180             markedClasses[className] = new MarkedClass(className);
0181 
0182         MarkedObject* object = nullptr;
0183         if (objectType == MarkedObject::Type::Sentence) {
0184             QJsonObject sentenceObj = jsonObj["st"].toObject();
0185             object = new Sentence(markedClasses[className], sentenceObj["x"].toString().toDouble(), sentenceObj["y"].toString().toDouble());
0186         }
0187         else if (objectType == MarkedObject::Type::Polygon) {
0188             QVector<QPointF> polygonPoints;
0189             for (const QJsonValue& unitObj : jsonObj["Polygon"].toArray()) {
0190                 QJsonObject pointJson = unitObj["pt"].toObject();
0191                 QPointF pointObj(pointJson["x"].toString().toDouble(), pointJson["y"].toString().toDouble());
0192                 polygonPoints << pointObj;
0193             }
0194             object = new Polygon(markedClasses[className], polygonPoints);
0195         }
0196 
0197         if (object != nullptr)
0198             savedObjects << object;
0199     }
0200 
0201     return savedObjects;
0202 }
0203 
0204 QVector<MarkedObject*> Serializer::readXML(const QString& filepath)
0205 {
0206     QVector<MarkedObject*> savedObjects;
0207     QHash<QString, MarkedClass*> markedClasses;
0208 
0209     QByteArray data = getData(filepath);
0210 
0211     QXmlStreamReader xmlReader(data);
0212     xmlReader.readNextStartElement();
0213 
0214     auto readNextXY = [&]() {
0215         xmlReader.readNextStartElement();
0216         double x = xmlReader.readElementText().toDouble();
0217         xmlReader.readNextStartElement();
0218         double y = xmlReader.readElementText().toDouble();
0219         xmlReader.skipCurrentElement();
0220 
0221         return QPair<int, int>(x, y);
0222     };
0223 
0224     while (!xmlReader.atEnd()) {
0225         QXmlStreamReader::TokenType token = xmlReader.readNext();
0226         if (token == QXmlStreamReader::StartElement) {
0227             xmlReader.readNextStartElement();
0228 
0229             QString className = xmlReader.readElementText();
0230 
0231             if (!markedClasses.contains(className))
0232                 markedClasses[className] = new MarkedClass(className);
0233 
0234             xmlReader.readNextStartElement();
0235 
0236             MarkedObject* object;
0237             if (xmlReader.name() == "Polygon") {
0238                 QVector<QPointF> polygonPoints;
0239 
0240                 while (xmlReader.name() != "object") {
0241                     if (xmlReader.name() == "pt") {
0242                         QPair<int, int> pointXY = readNextXY();
0243                         polygonPoints << QPointF(pointXY.first, pointXY.second);
0244                     }
0245                     xmlReader.readNextStartElement();
0246                 }
0247 
0248                 object = new Polygon(markedClasses[className], polygonPoints);
0249             }
0250             else if (xmlReader.name() == "st") {
0251                 QPair<int, int> sentenceBeginEnd = readNextXY();
0252                 object = new Sentence(markedClasses[className], sentenceBeginEnd.first, sentenceBeginEnd.second);
0253             }
0254 
0255             savedObjects << object;
0256         }
0257     }
0258 
0259     return savedObjects;
0260 }
0261 
0262 QByteArray Serializer::getData(const QString& filepath)
0263 {
0264     QFile file(filepath);
0265     file.open(QIODevice::ReadOnly|QIODevice::Text);
0266     QByteArray data = file.readAll();
0267     file.close();
0268 
0269     return data;
0270 }
0271 
0272 bool Serializer::write(const QString &filepath, const QVector<MarkedObject*>& objects, OutputType outputType)
0273 {
0274     if (!objects.isEmpty()) {
0275         QString document = serialize(objects, outputType);
0276 
0277         if (!document.isEmpty()) {
0278             QString outputFilename = FileUtils::placeSuffix(filepath, outputType);
0279             QFile file(outputFilename);
0280             if (file.open(QIODevice::WriteOnly|QIODevice::Text))
0281                 return file.write(document.toUtf8()) != -1;
0282         }
0283     }
0284 
0285     return false;
0286 }