File indexing completed on 2024-05-12 15:26:43

0001 /***************************************************************************
0002     File                 : Segments.cpp
0003     Project              : LabPlot
0004     Description          : Contains methods to trace curve of image/plot
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2015 by Ankit Wagadre (wagadre.ankit@gmail.com)
0007  ***************************************************************************/
0008 /***************************************************************************
0009  *                                                                         *
0010  *  This program is free software; you can redistribute it and/or modify   *
0011  *  it under the terms of the GNU General Public License as published by   *
0012  *  the Free Software Foundation; either version 2 of the License, or      *
0013  *  (at your option) any later version.                                    *
0014  *                                                                         *
0015  *  This program is distributed in the hope that it will be useful,        *
0016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0018  *  GNU General Public License for more details.                           *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the Free Software           *
0022  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0023  *   Boston, MA  02110-1301  USA                                           *
0024  *                                                                         *
0025  ***************************************************************************/
0026 
0027 #include "Segments.h"
0028 #include "backend/datapicker/Segment.h"
0029 #include "backend/datapicker/ImageEditor.h"
0030 
0031 #include <QGraphicsScene>
0032 #include <QImage>
0033 #include <QGraphicsItem>
0034 
0035 /**
0036  * \class Segments
0037  * \brief container to open image/plot.
0038  *
0039  * this class is a container for all of the segment. a segment is a set
0040  * of linked lines that run along a curve in the original image. the
0041  * main complication is that curves in the original image cross each other
0042  * and other things like grid lines. we rely on the user to link
0043  * multiple segments together to get points along the entire curve length
0044  * * \ingroup datapicker
0045  */
0046 
0047 Segments::Segments(DatapickerImage* image): m_image(image) {
0048 }
0049 
0050 /*!
0051     segments are built when the original image is loaded. they start out hidden
0052      and remain so until showSegments is called
0053 */
0054 void Segments::makeSegments(QImage &imageProcessed) {
0055 //  QElapsedTimer timer;
0056 //  timer.start();
0057     clearSegments();
0058 
0059     const int width = imageProcessed.width();
0060     const int height = imageProcessed.height();
0061 
0062     // for each new column of pixels, loop through the runs. a run is defined as
0063     // one or more colored pixels that are all touching, with one uncolored pixel or the
0064     // image boundary at each end of the set. for each set in the current column, count
0065     // the number of runs it touches in the adjacent (left and right) columns. here is
0066     // the pseudocode:
0067     //   if ((L > 1) || (R > 1))
0068     //     "this run is at a branch point so ignore the set"
0069     //   else
0070     //     if (L == 0)
0071     //       "this run is the start of a new segment"
0072     //     else
0073     //       "this run is appended to the segment on the left
0074 
0075     bool* lastBool = new bool [height];
0076     bool* currBool = new bool [height];
0077     bool* nextBool = new bool [height];
0078     Segment** lastSegment = new Segment* [(size_t)height];
0079     Segment** currSegment = new Segment* [(size_t)height];
0080     loadSegment(lastSegment, height);
0081 
0082     //initialize one column of boolean flags using the pixels of the specified column
0083     for (int y = 0; y < height; ++y) {
0084         lastBool[y] = false;
0085         currBool[y] = ImageEditor::processedPixelIsOn(imageProcessed, 0, y);
0086         nextBool[y] = ImageEditor::processedPixelIsOn(imageProcessed, 1, y);
0087     }
0088 
0089     for (int x = 0; x < width; ++x) {
0090         matchRunsToSegments(x, height, lastBool, lastSegment, currBool, currSegment, nextBool);
0091 
0092         // get ready for next column
0093         for (int y = 0; y < height; ++y) {
0094             lastBool[y] = currBool[y];
0095             currBool[y] = nextBool[y];
0096         }
0097 
0098         if (x + 1 < width) {
0099             for (int y = 0; y < height; ++y)
0100                 nextBool[y] = ImageEditor::processedPixelIsOn(imageProcessed, x+1, y);
0101         }
0102         scrollSegment(lastSegment, currSegment, height);
0103     }
0104 
0105     delete[] lastBool;
0106     delete[] currBool;
0107     delete[] nextBool;
0108     delete[] lastSegment;
0109     delete[] currSegment;
0110 //  qDebug() << "Made segments in " << timer.elapsed() << "ms";
0111 }
0112 
0113 /*!
0114     scroll the segment pointers of the right column into the left column
0115 */
0116 void Segments::scrollSegment(Segment** left, Segment** right, int height) {
0117     for (int y = 0; y < height; ++y)
0118         left [y] = right [y];
0119 }
0120 
0121 /*!
0122     identify the runs in a column, and connect them to segments
0123 */
0124 void Segments::matchRunsToSegments(int x, int height, bool* lastBool, Segment** lastSegment,
0125                                    bool* currBool, Segment** currSegment, bool* nextBool) {
0126     loadSegment(currSegment, height);
0127 
0128     int yStart = 0;
0129     bool inRun = false;
0130     for (int y = 0; y < height; ++y) {
0131         if (!inRun && currBool [y]) {
0132             inRun = true;
0133             yStart = y;
0134         }
0135 
0136         if ((y + 1 >= height) || !currBool [y + 1]) {
0137             if (inRun)
0138                 finishRun(lastBool, nextBool, lastSegment, currSegment, x, yStart, y, height);
0139 
0140             inRun = false;
0141         }
0142     }
0143 
0144     removeUnneededLines(lastSegment, currSegment, height);
0145 }
0146 
0147 /*!
0148     remove unneeded lines belonging to segments that just finished in the previous column.
0149 */
0150 void Segments::removeUnneededLines(Segment** lastSegment, Segment** currSegment, int height) {
0151     Segment* segLast = nullptr;
0152     for (int yLast = 0; yLast < height; ++yLast) {
0153         if (lastSegment [yLast] && (lastSegment [yLast] != segLast)) {
0154             segLast = lastSegment [yLast];
0155 
0156             bool found = false;
0157             for (int yCur = 0; yCur < height; ++yCur)
0158                 if (segLast == currSegment [yCur]) {
0159                     found = true;
0160                     break;
0161                 }
0162 
0163             if (!found) {
0164                 if (segLast->length < m_image->minSegmentLength()) {
0165                     // remove whole segment since it is too short
0166                     m_image->scene()->removeItem(segLast->graphicsItem());
0167                     segments.removeOne(segLast);
0168                 }
0169             }
0170         }
0171     }
0172 }
0173 
0174 /*!
0175     initialize one column of segment pointers
0176 */
0177 void Segments::loadSegment(Segment** columnSegment, int height) {
0178     for (int y = 0; y < height; ++y)
0179         columnSegment [y] = nullptr;
0180 }
0181 
0182 void Segments::clearSegments() {
0183     for (auto* seg : segments)
0184         m_image->scene()->removeItem(seg->graphicsItem());
0185 
0186     segments.clear();
0187 }
0188 
0189 /*!
0190     set segments visible
0191 */
0192 void Segments::setSegmentsVisible(bool on) {
0193     for (auto* seg : segments)
0194         seg->setVisible(on);
0195 }
0196 
0197 void Segments::setAcceptHoverEvents(bool on) {
0198     for (auto* seg : segments) {
0199         QGraphicsItem *item = seg->graphicsItem();
0200         item->setAcceptHoverEvents(on);
0201         item->setFlag(QGraphicsItem::ItemIsSelectable, on);
0202     }
0203 }
0204 
0205 /*!
0206     process a run of pixels. if there are fewer than two adjacent pixel runs on
0207     either side, this run will be added to an existing segment, or the start of
0208     a new segment
0209 */
0210 void Segments::finishRun(bool* lastBool, bool* nextBool, Segment** lastSegment, Segment** currSegment,
0211                          int x, int yStart, int yStop, int height) {
0212 
0213     // count runs that touch on the left
0214     if (adjacentRuns(lastBool, yStart, yStop, height) > 1)
0215         return;
0216 
0217     // count runs that touch on the right
0218     if (adjacentRuns(nextBool, yStart, yStop, height) > 1)
0219         return;
0220 
0221     Segment* seg;
0222     int y = (int) ((yStart + yStop) / 2);
0223     if (adjacentSegments(lastSegment, yStart, yStop, height) == 0) {
0224         seg = new Segment(m_image);
0225         QLine* line = new QLine(QPoint(x, y),QPoint( x, y));
0226         seg->path.append(line);
0227         seg->yLast  = y;
0228         segments.append(seg);
0229     } else {
0230         // this is the continuation of an existing segment
0231         seg = adjacentSegment(lastSegment, yStart, yStop, height);
0232         QLine* line = new QLine(QPoint(x - 1, seg->yLast),QPoint( x, y));
0233         seg->length  += abs(1 + (seg->yLast - y)*(seg->yLast - y));
0234         seg->path.append(line);
0235         seg->yLast = y;
0236     }
0237 
0238     seg->retransform();
0239 
0240     for (int y = yStart; y <= yStop; ++y)
0241         currSegment [y] = seg;
0242 }
0243 
0244 /*!
0245     return the number of runs adjacent to the pixels from yStart to yStop (inclusive)
0246 */
0247 int Segments::adjacentRuns(bool* columnBool, int yStart, int yStop, int height) {
0248     int runs = 0;
0249     bool inRun = false;
0250     for (int y = yStart - 1; y <= yStop + 1; ++y) {
0251         if ((0 <= y) && (y < height)) {
0252             if (!inRun && columnBool [y]) {
0253                 inRun = true;
0254                 ++runs;
0255             } else if (inRun && !columnBool [y])
0256                 inRun = false;
0257         }
0258     }
0259 
0260     return runs;
0261 }
0262 
0263 /*!
0264     find the single segment pointer among the adjacent pixels from yStart-1 to yStop+1
0265 */
0266 Segment* Segments::adjacentSegment(Segment** lastSegment, int yStart, int yStop, int height) {
0267     for (int y = yStart - 1; y <= yStop + 1; ++y) {
0268         if ((0 <= y) && (y < height))
0269             if (lastSegment [y])
0270                 return lastSegment [y];
0271     }
0272 
0273     return nullptr;
0274 }
0275 
0276 /*!
0277     return the number of segments adjacent to the pixels from yStart to yStop (inclusive)
0278 */
0279 int Segments::adjacentSegments(Segment** lastSegment, int yStart, int yStop, int height) {
0280     int count = 0;
0281     bool inSegment = false;
0282     for (int y = yStart - 1; y <= yStop + 1; ++y) {
0283         if ((0 <= y) && (y < height)) {
0284             if (!inSegment && lastSegment [y]) {
0285                 inSegment = true;
0286                 ++count;
0287             } else if (inSegment && !lastSegment [y])
0288                 inSegment = false;
0289         }
0290     }
0291 
0292     return count;
0293 }