diff --git a/Code/Seg/BSTools/bsaccumulatoritem.cpp b/Code/Seg/BSTools/bsaccumulatoritem.cpp new file mode 100755 index 0000000000000000000000000000000000000000..3ecf8c027892b6a754b2b1bf000a7b6e65d5bb70 --- /dev/null +++ b/Code/Seg/BSTools/bsaccumulatoritem.cpp @@ -0,0 +1,168 @@ +#include <QtGui> +#include <iostream> +#include <cmath> +#include "bsaccumulatoritem.h" + +using namespace std; + + + +const int BSAccumulatorItem::DEFAULT_PEN_WIDTH = 1; +const int BSAccumulatorItem::LEFT_MARGIN = 16; +const int BSAccumulatorItem::TEXT_HEIGHT = 16; + + +BSAccumulatorItem::BSAccumulatorItem (BSDetector *sd, int w, int h) +{ + detector = sd; + width = w; + height = h; + selaccu = 1; + map[0] = NULL; + map[1] = NULL; + solution[0] = -1; + solution[1] = -1; + resol[0] = 1.; + resol[1] = 1.; + mask = NULL; + verbose = true; + infoPen = QPen (Qt::red, DEFAULT_PEN_WIDTH, Qt::SolidLine, + Qt::RoundCap, Qt::RoundJoin); + scalePen = QPen (Qt::red, 2); + markPen = QPen (Qt::blue, 4); +} + + +BSAccumulatorItem::~BSAccumulatorItem () +{ + delete [] map[0]; + delete [] map[1]; +} + + +QRectF BSAccumulatorItem::boundingRect () const +{ + return QRectF (0, 0, width, height); +} + + +void BSAccumulatorItem::paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (option); + Q_UNUSED (widget); + + if (! detector->isFiltering (selaccu)) + { + painter->fillRect (0, 0, width, height, QBrush (Qt::black)); + return; + } + + // Updates the grid + LineSpaceFilter *hugues = detector->getFilter (selaccu); + int nbc = hugues->width (); + int nbl = hugues->height (); + int cw = width / nbc; + int ch = height / nbl; + + // Updates the accumulator + if (map[selaccu] == NULL) map[selaccu] = new uchar[nbc * nbl]; + hugues->getAccumulator (map[selaccu], solution); + resol[0] = hugues->angularResolution () * 180. / M_PI; + resol[1] = hugues->distanceResolution (); + mask = hugues->getMask (); + + // Draws the accumulator + if (solution[0] >= 0) + { + uchar *pix = map[selaccu]; + for (int i = 0; i < nbc * nbl; i++, pix++) + painter->fillRect ((i % nbc) * cw, (nbl - 1 - i / nbc) * ch, cw, ch, + QBrush (QColor ((int) *pix, (int) *pix, (int) *pix))); + + // Marks the filtering cells + if (mask != NULL) + { + painter->setPen (markPen); + bool *maski = mask; + for (int j = 0; j < nbl; j++) + for (int i = 0; i < nbc; i++) + if (*maski++) + painter->drawPoint ((i + 0.5) * cw, (nbl - j - 0.5) * ch); + } + + if (verbose) + { + // Displays the scale (one radian / one pixel) + painter->setPen (scalePen); + int sh = (int) (ch / resol[1] + 0.5); + painter->drawRect (0, height - 1 - sh, (int) (cw / resol[0] + 0.5), sh); + + // Displays information + initText (painter); + addText (painter, QString ("S size : ") + QString::number (nbc) + + QString ("x") + QString::number (nbl)); + addText (painter, QString ("R resolution : ") + + QString::number (resol[0]) + QString (" pixels x ") + + QString::number (resol[1]) + QString (" degrees")); + addText (painter, QString ("P sub-pixellisation level : ") + + QString::number (hugues->getSubpix ())); + addText (painter, QString ("F selectivity : ") + + QString::number (hugues->getSelectionThreshold ()) + + QString ("% of max vote")); + } + } +} + + +bool BSAccumulatorItem::resizeAccumulator (bool larger) +{ + if (! detector->isFiltering (selaccu)) return false; + LineSpaceFilter *hugues = detector->getFilter (selaccu); + bool res = hugues->resize (larger); + if (res) + { + delete [] map[selaccu]; + map[selaccu] = new uchar[hugues->width () * hugues->height ()]; + } + return (res); +} + + +bool BSAccumulatorItem::zoomAccumulator (bool in) +{ + if (! detector->isFiltering (selaccu)) return false; + LineSpaceFilter *hugues = detector->getFilter (selaccu); + return (hugues->zoom (in)); +} + + +bool BSAccumulatorItem::subpixellise (bool larger) +{ + if (! detector->isFiltering (selaccu)) return false; + LineSpaceFilter *hugues = detector->getFilter (selaccu); + return (hugues->subpixellise (larger)); +} + + +bool BSAccumulatorItem::setFilterSelectivity (bool larger) +{ + if (! detector->isFiltering (selaccu)) return false; + LineSpaceFilter *hugues = detector->getFilter (selaccu); + return (hugues->setSelectivity (larger)); +} + + +void BSAccumulatorItem::initText (QPainter *painter) +{ + painter->setPen (infoPen); + textOffset = 0; +} + + +void BSAccumulatorItem::addText (QPainter *painter, const QString &text) +{ + textOffset += TEXT_HEIGHT; + painter->drawText (LEFT_MARGIN, textOffset, text); +} diff --git a/Code/Seg/BSTools/bsaccumulatoritem.h b/Code/Seg/BSTools/bsaccumulatoritem.h new file mode 100755 index 0000000000000000000000000000000000000000..25368fb65b8f12233563c7745040b703ed1e1287 --- /dev/null +++ b/Code/Seg/BSTools/bsaccumulatoritem.h @@ -0,0 +1,130 @@ +#ifndef BS_ACCUMULATOR_ITEM_H +#define BS_ACCUMULATOR_ITEM_H + +#include <QGraphicsItem> +#include "bsdetector.h" + + +/** + * @class BSAccumulatorItem bsaccumulatoritem.h + * \brief Hough accumulator grid display and control. + * \author {P. Even} + */ +class BSAccumulatorItem : public QGraphicsItem +{ + +public: + + /** + * \brief Creates a Hough accumulator grid. + * @param sd Associated blurred segment detector. + * @param w Accumulator width. + * @param h Accumulator height. + */ + BSAccumulatorItem (BSDetector *sd, int w, int h); + + /** Deletes the Hough accumulator grid. + */ + ~BSAccumulatorItem (); + + /** \brief Inquires if the pre-filter accumulator is displayed. + */ + inline bool isPrefilterDisplayed () const {return (selaccu == 0); } + + /** \brief Switches the displayed accumulator filter. + */ + inline void switchAccumulator () { selaccu = (selaccu != 0 ? 0 : 1); } + + /** \brief Returns the size of the filter accumulator graphics item. + */ + QRectF boundingRect () const; + + /** \brief Draws the accumulator of the selected filter. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + /** + * \brief Switches on or off the information text display modality. + */ + inline void switchInfoDisplay () { verbose = ! verbose; } + + /** \brief Resizes the accumulator array. + * @param larger Sets larger if true, smaller otherwise. + */ + bool resizeAccumulator (bool larger); + + /** \brief Zooms in or out the accumulator array. + * @param in Zooms in if true, out otherwise. + */ + bool zoomAccumulator (bool in); + + /** \brief Modifies the subpixellisation of the accumulator array. + * @param in Sets larger if true, smaller otherwise. + */ + bool subpixellise (bool larger); + + /** \brief Modifies the filter selectivity. + * @param larger Makes the filter more selective if true, less otherwise. + */ + bool setFilterSelectivity (bool larger); + + +private: + + /** Default value for pen width. */ + static const int DEFAULT_PEN_WIDTH; + /** Left margin for information text. */ + static const int LEFT_MARGIN; + /** Information text height. */ + static const int TEXT_HEIGHT; + + + /** Blurred segment detector. */ + BSDetector *detector; + /** Accumulator displayed (0 = prefilter, 1 = final filter). */ + int selaccu; + + /** Accumulator width. */ + int width; + /** Accumulator height. */ + int height; + /** Contents of the accumulator. + * The array of vote counts for each cell. */ + uchar *map[2]; + /** Coordinates of the cell with the highest vote. */ + int solution[2]; + /** Accumulator scale in pixels. + * One radian for the angles, and one pixel for the distances. */ + double resol[2]; + /** Contents of the applied accumulator mask. + * An array of booleans values for each cell, + * that are set to true if the cell belongs to the mask. */ + bool *mask; + + /** Information text modality. */ + bool verbose; + /** Information text style. */ + QPen infoPen; + /** Information text vertical offset. */ + int textOffset; + /** Scale display style. */ + QPen scalePen; + /** Mark display style. */ + QPen markPen; + + + /** + * \brief Initializes the text display (color and position). + * @param painter : Painter to be decorated. + */ + void initText (QPainter *painter); + + /** + * \brief Paints a new text in the graphics item (updates text position). + * @param painter : Painter to be decorated. + * @param text : Text to be displayed. + */ + void addText (QPainter *painter, const QString &text); +}; +#endif diff --git a/Code/Seg/BSTools/bsaccumulatorview.cpp b/Code/Seg/BSTools/bsaccumulatorview.cpp new file mode 100755 index 0000000000000000000000000000000000000000..4610b547726b700fc5d75e4a891bdea85ce73a92 --- /dev/null +++ b/Code/Seg/BSTools/bsaccumulatorview.cpp @@ -0,0 +1,79 @@ +#include <QtGui> +#include <iostream> +#include "bsaccumulatorview.h" + +using namespace std; + + +const int BSAccumulatorView::CELL_SIZE = 12; + + +BSAccumulatorView::BSAccumulatorView (BSDetector *sd) +{ + int w = LineSpaceFilter::DEFAULT_NB_ANG_CELLS * CELL_SIZE; + int h = LineSpaceFilter::DEFAULT_NB_DIST_CELLS * CELL_SIZE; + setBackgroundBrush (QBrush (Qt::yellow)); + setScene (new QGraphicsScene (0, 0, w, h)); + grid = new BSAccumulatorItem (sd, w, h); + scene()->addItem (grid); + setWindowTitle (grid->isPrefilterDisplayed () ? + "Pre-filter accumulator" : "Final filter accumulator"); +} + + +BSAccumulatorView::~BSAccumulatorView () +{ + scene()->removeItem (grid); + delete grid; +} + + +void BSAccumulatorView::paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (painter); + Q_UNUSED (option); + Q_UNUSED (widget); +} + + +bool BSAccumulatorView::processKeyEvent (QKeyEvent *event) +{ + bool processed = false; + switch (event->key ()) + { + case Qt::Key_I : + grid->switchAccumulator (); + setWindowTitle (grid->isPrefilterDisplayed () ? + "Pre-filter accumulator" : "Final filter accumulator"); + processed = true; + break; + + case Qt::Key_V : + grid->switchInfoDisplay (); + processed = true; + break; + + case Qt::Key_S : + processed = grid->resizeAccumulator ( + (event->modifiers () & Qt::ShiftModifier) == 0); + break; + + case Qt::Key_R : + processed = grid->zoomAccumulator ( + (event->modifiers () & Qt::ShiftModifier) == 0); + break; + + case Qt::Key_P : + processed = grid->subpixellise ( + (event->modifiers () & Qt::ShiftModifier) != 0); + break; + + case Qt::Key_F : + processed = grid->setFilterSelectivity ( + (event->modifiers () & Qt::ShiftModifier) == 0); + break; + } + return processed; +} diff --git a/Code/Seg/BSTools/bsaccumulatorview.h b/Code/Seg/BSTools/bsaccumulatorview.h new file mode 100755 index 0000000000000000000000000000000000000000..75ccfebed3120a1a9cd19fcc9bc3b1a47b55e021 --- /dev/null +++ b/Code/Seg/BSTools/bsaccumulatorview.h @@ -0,0 +1,42 @@ +#ifndef BS_ACCUMULATOR_H +#define BS_ACCUMULATOR_H + +#include <QGraphicsView> +#include "bsaccumulatoritem.h" + + +class BSAccumulatorView : public QGraphicsView +{ + +public: + + /** + * \brief Creates an accumulator analyzer. + */ + BSAccumulatorView (BSDetector *sd); + + /** + * \brief Deletes the accumulator analyzer. + */ + ~BSAccumulatorView (); + + /** + * \brief Redraws the accumulator analyzer. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + /** + * \brief Processes key pressed events. + */ + bool processKeyEvent (QKeyEvent *event); + +protected: + +private: + static const int CELL_SIZE; + BSAccumulatorItem *grid; + +}; + +#endif diff --git a/Code/Seg/BSTools/bsdetectionwidget.cpp b/Code/Seg/BSTools/bsdetectionwidget.cpp new file mode 100755 index 0000000000000000000000000000000000000000..375f19a803388e2b4f99e4791cb82d5a5e21b4d4 --- /dev/null +++ b/Code/Seg/BSTools/bsdetectionwidget.cpp @@ -0,0 +1,762 @@ +#include <QtGui> +#include <iostream> +#include <cstdlib> +#include <ctime> +#include "bsdetectionwidget.h" + +using namespace std; + + + +const int BSDetectionWidget::MAX_WIDTH_TUNING = 400; +const int BSDetectionWidget::DETECTION_LACKS_TUNING = 20; +const int BSDetectionWidget::DEFAULT_PEN_WIDTH = 1; + + + +BSDetectionWidget::BSDetectionWidget (QWidget *parent) +{ + Q_UNUSED (parent); + + // Sets default user interface parameters + setFocus (); + grabKeyboard (); + fixed = false; + + // Sets initial values for the gradient map + gMap = NULL; + + // Initializes the auxiliary views + accuview = NULL; + strucview = NULL; + profileview = NULL; + + alternate = 0; + verbose = false; + + instanceOfAll = 0; +} + + +BSDetectionWidget::~BSDetectionWidget () +{ + if (accuview != NULL) delete accuview; + if (strucview != NULL) delete strucview; +} + + +QSize BSDetectionWidget::openImage (const QString &fileName, int type) +{ + QSize newSize (0, 0); + loadedImage.load (fileName); + width = loadedImage.width (); + height = loadedImage.height (); + newSize = loadedImage.size (); + + augmentedImage = loadedImage; + if (gMap != NULL) delete gMap; + gMap = new VMap (width, height, getBitmap (augmentedImage), type); + detector.setGradientMap (gMap); + + update (); + if (profileview != NULL) profileview->setImage (&loadedImage, gMap); + if (strucview != NULL) strucview->setGradientImage (gMap); + + return newSize; +} + + +int **BSDetectionWidget::getBitmap (const QImage &image) +{ + int w = image.width (); + int h = image.height (); + + int **tabImage = new int*[h]; + for (int i = 0; i < h; i++) + { + tabImage[i] = new int[w]; + for(int j = 0; j < w; j++) + { + QColor c = QColor (image.pixel (j, h - i - 1)); + tabImage[i][j] = c.value (); + } + } + return tabImage; +} + + +bool BSDetectionWidget::saveAugmentedImage (const QString &fileName, + const char *fileFormat) +{ + QImage aImage = augmentedImage; + return (aImage.save (fileName, fileFormat)); +} + + +void BSDetectionWidget::clearImage () +{ + augmentedImage.fill (qRgb (255, 255, 255)); + update (); +} + + +void BSDetectionWidget::paintEvent (QPaintEvent *) +{ + QPainter painter (this); + painter.drawImage (QPoint (0, 0), augmentedImage); +} + + +void BSDetectionWidget::storeExtractedSegment () +{ + BlurredSegment *bs = detector.getBlurredSegment (1); + if (bs != NULL) + { + ExtractedSegment es; + es.bs = bs; + es.p1 = p1; + es.p2 = p2; + extractedSegments.push_back (es); + detector.preserveFormerBlurredSegment (); + } +} + + +void BSDetectionWidget::displayExtractedSegments () +{ + augmentedImage = loadedImage; + QPainter painter (&augmentedImage); + if (! extractedSegments.empty ()) + { + vector<ExtractedSegment>::iterator it = extractedSegments.begin (); + while (it != extractedSegments.end ()) + { + vector<Pt2i> bnd; + DigitalStraightSegment *dss = it->bs->getSegment (); + dss->getBoundPoints (bnd, false, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + dss->getBoundPoints (bnd, true, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + bnd = it->bs->getAllPoints (); + drawListOfPixels (bnd, Qt::blue, painter); + drawLine (it->p1, it->p2, Qt::white, painter); + it ++; + } + } + update (QRect (QPoint (0, 0), QPoint (width, height))); +} + + +void BSDetectionWidget::clearExtractedSegments () +{ + vector<ExtractedSegment>::iterator it = extractedSegments.begin (); + while (it != extractedSegments.end ()) + delete ((it++)->bs->getSegment ()); + extractedSegments.clear (); +} + + +void BSDetectionWidget::closeAccuAnalyzer () +{ + if (accuview != NULL) + { + accuview->close (); + delete accuview; + accuview = NULL; + } +} + + +void BSDetectionWidget::closePixelAnalyzer () +{ + if (strucview != NULL) + { + strucview->close (); + delete strucview; + strucview = NULL; + } +} + + +void BSDetectionWidget::closeProfileAnalyzer () +{ + if (profileview != NULL) + { + profileview->close (); + delete profileview; + profileview = NULL; + } +} + + +void BSDetectionWidget::switchAccuAnalyzer () +{ + if (accuview != NULL) + { + accuview->close (); + delete accuview; + accuview = NULL; + } + else + { + accuview = new BSAccumulatorView (&detector); + accuview->show (); + } +} + + +void BSDetectionWidget::switchPixelAnalyzer () +{ + if (strucview != NULL) + { + strucview->close (); + delete strucview; + strucview = NULL; + } + else + { + strucview = new BSStructureView (&loadedImage, &detector); + strucview->setGradientImage (gMap); + strucview->show (); + } +} + + +void BSDetectionWidget::switchProfileAnalyzer () +{ + if (profileview != NULL) + { + profileview->close (); + delete profileview; + profileview = NULL; + } + else + { + profileview = new BSProfileView (); + profileview->setImage (&loadedImage, gMap); + if (! p1.equals (p2)) profileview->buildScans (p1, p2); + profileview->show (); + } +} + + +void BSDetectionWidget::mousePressEvent (QMouseEvent *event) +{ + this->p1 = Pt2i (event->pos().x (), height - 1 - event->pos().y()); +} + + +void BSDetectionWidget::mouseReleaseEvent (QMouseEvent *event) +{ + fixed = true; + this->p2 = Pt2i (event->pos().x (), height - 1 - event->pos().y()); +alternate = 0; + cerr << "p1 defined: " << p1.x () << " " << p1.y () << endl; + cerr << "p2 defined: " << p2.x () << " " << p2.y () << endl; + extract (); +} + + +void BSDetectionWidget::mouseMoveEvent (QMouseEvent *event) +{ + fixed = false; + this->p2 = Pt2i (event->pos().x (), height - 1 - event->pos().y ()); + if (verbose) cerr << "(" << p1.x () << ", " << p1.y () << ") (" + << p2.x () << ", " << p2.y () << ")" << endl; + if (p1.manhattan (p2) > 5 + && (width > p2.x() && height > p2.y() + && p2.x() > 0 && p2.y() > 0)) + { + extract (); + } +} + + +void BSDetectionWidget::keyPressEvent (QKeyEvent *event) +{ + if (isActiveWindow ()) switch (event->key ()) + { + case Qt::Key_U : + cerr << "p1 update: " << p1.x () << " " << p1.y () << endl; + cerr << "p2 update: " << p2.x () << " " << p2.y () << endl; + extract (true); + break; + + case Qt::Key_M : // Multi-selection switch + detector.switchMultiSelection (); + cout << "Multi-selection " + << (detector.isMultiSelection () ? "on" : "off") << endl; + break; + + case Qt::Key_Y : // Initial detection extension limitation + detector.switchInitialBounding (); + extract (true); + cout << "Initial step max extension = " + << detector.initialDetectionMaxExtent () << endl; + break; + + case Qt::Key_D : // Density test at initial step + detector.switchDensityTest (); + extract (true); + cout << "Density test : " + << (detector.isSetDensityTest () ? "on" : "off") << endl; + break; + + case Qt::Key_W : // Input max width + detector.setInputMaxWidth (detector.getInputMaxWidth () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + extract (true); + cout << "Input max width = " << detector.getInputMaxWidth () << endl; + break; + + case Qt::Key_L : // Output blurred segment min size + detector.setBSminSize (detector.getBSminSize () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + extract (true); + cout << "Output BS min size = " << detector.getBSminSize () << endl; + break; + + case Qt::Key_H : // Pixel lack tolerence + detector.setPixelLackTolerence (detector.getPixelLackTolerence () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + extract (true); + cout << "Lack tolerence = " << detector.getPixelLackTolerence () + << " pixels" << endl; + break; + + case Qt::Key_N : // Automatic detection grid resolution + detector.setAutoGridResolution (detector.getAutoGridResolution () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + cout << "Auto grid resolution = " + << detector.getAutoGridResolution () << " pixels" << endl; + break; + + case Qt::Key_Q : + detector.switchAutoRestart (); + extract (true); + cout << "Segment continuation after = " + << detector.getRestartOnLack () << " pixels" << endl; + break; + + case Qt::Key_T : + detector.toggleThinning (); + extract (true); + cout << "Thinning " + << (detector.isThinningActivated () ? "on" : "off") << endl; + break; + + case Qt::Key_X : + detector.switchAutoWidth (); + cout << "Final step max width " << (detector.autoWidthOn () ? + "fitted to initial segment" : "left unchanged") << endl; + extract (true); + break; + + case Qt::Key_S : + detector.switchDynamicScans (); + cout << (detector.dynamicScansOn () ? + "Dynamic scans" : "Static scans") << endl; + extract (true); + break; + + case Qt::Key_O : + detector.switchOrthoScans (); + cout << (detector.orthoScansOn () ? + "Orthographic scans" : "Directional scans") << endl; + extract (true); + break; + + case Qt::Key_F : + detector.switchFiltering (1); + cout << "Final filtering " + << (detector.isFiltering (1) ? "on" : "off") << endl; + extract (true); + break; + + case Qt::Key_P : + detector.switchFiltering (0); + cout << "Pre-filtering " + << (detector.isFiltering (0) ? "on" : "off") << endl; + extract (true); + break; + + case Qt::Key_G : + detector.switchConnectivityConstraint (); + cout << "Connectivity constraint " + << (detector.isSetConnectivityConstraint () ? "on" : "off") << endl; + extract (true); + break; + + case Qt::Key_C : + if (event->modifiers () & Qt::ControlModifier) + storeExtractedSegment (); + break; + + case Qt::Key_V : + if (event->modifiers () & Qt::ControlModifier) + displayExtractedSegments (); + else switchVerbose (); + break; + + case Qt::Key_Z : + if (event->modifiers () & Qt::ControlModifier) + clearExtractedSegments (); + break; + + case Qt::Key_E : + if (event->modifiers () & Qt::ShiftModifier) + detector.invertEdgeDirection (); + else detector.switchEdgeDirectionConstraint (); + switch (detector.edgeDirectionConstraint ()) + { + case 0 : + cout << "Stroke detection" << endl; + break; + case 1 : + cout << "Edge detection" << endl; + break; + case -1 : + cout << "Opposite edge detection" << endl; + break; + } + extract (true); + break; + + case Qt::Key_1 : + switchPixelAnalyzer (); + break; + + case Qt::Key_2 : + switchAccuAnalyzer (); + break; + + case Qt::Key_3 : + switchProfileAnalyzer (); + break; + + case Qt::Key_6 : + displayNextOfAll (); + break; + + case Qt::Key_7 : + extractAll (); + break; + + case Qt::Key_8 : + alternateTest (); + break; + + case Qt::Key_9 : + performanceTest (); + break; + + case Qt::Key_0 : + localTest (); + break; + } + else if (strucview != NULL && strucview->isActiveWindow ()) + { + if (strucview->processKeyEvent (event)) extract (true); + } + else if (accuview != NULL && accuview->isActiveWindow ()) + { + if (accuview->processKeyEvent (event)) extract (true); + } + else if (profileview != NULL && profileview->isActiveWindow ()) + { + if (profileview->processKeyEvent (event)) extract (true); + } +} + + +void BSDetectionWidget::drawListOfPixels (vector<Pt2i> vectPixels, + QColor color, QPainter &painter) +{ + vector<Pt2i>::iterator iter = vectPixels.begin (); + while (iter != vectPixels.end ()) + { + Pt2i p = *iter; + painter.setPen (QPen (color, DEFAULT_PEN_WIDTH, Qt::SolidLine, + Qt::RoundCap, Qt::RoundJoin)); + if (p.x() < width && p.y() < height && p.x() >= 0 && p.y() >= 0) + painter.drawPoint (QPoint (p.x(), height - 1 - p.y())); // dec 1 + iter ++; + } +} + + +void BSDetectionWidget::drawListOfPixels (vector<Pt2i> vectPixels, + QPainter &painter) +{ + vector<Pt2i>::iterator iter = vectPixels.begin (); + while (iter != vectPixels.end ()) + { + Pt2i p = *iter; + painter.setPen (QPen (QBrush (loadedImage.pixel (p.x (), + loadedImage.height () - 1 - p.y ())), + DEFAULT_PEN_WIDTH, Qt::SolidLine, + Qt::RoundCap, Qt::RoundJoin)); + if (p.x() < width && p.y() < height && p.x() >= 0 && p.y() >= 0) + painter.drawPoint (QPoint (p.x(), height - 1 - p.y())); // dec 1 + iter ++; + } +} + + +void BSDetectionWidget::drawLine (const Pt2i from, const Pt2i to, + QColor color, QPainter &painter) +{ + int n; + Pt2i *pts = from.drawing (to, &n); + painter.setPen (QPen (color, DEFAULT_PEN_WIDTH, Qt::SolidLine, + Qt::RoundCap, Qt::RoundJoin)); + for (int i = 0; i < n; i++) + painter.drawPoint (QPoint (pts[i].x (), + height - 1 - pts[i].y ())); // dec 1 + delete [] pts; +} + + +void BSDetectionWidget::extract (bool withAllDisplay) +{ + if (! p1.equals (p2)) + { + fixed = withAllDisplay; + extract (); + } +} + + +void BSDetectionWidget::extract () +{ + augmentedImage = loadedImage; + QPainter painter (&augmentedImage); + if (p1.equals (p2)) + { + update (QRect (QPoint (0, 0), QPoint (width, height))); + fixed = false; + return; + } + drawLine (p1, p2, Qt::red, painter); + + if (detector.isMultiSelection ()) detector.multidetect (p1, p2); + else detector.detect (p1, p2); + + // Update auxiliary view if not dragging + if (fixed) + { + if (profileview != NULL) + { + profileview->buildScans (p1, p2); + profileview->scene()->update (); + } + if (accuview != NULL) accuview->scene()->update (); + if (strucview != NULL) strucview->scene()->update (); + } + + if (detector.isMultiSelection ()) + { + vector<BlurredSegment *> mbs = detector.getBlurredSegments (); + vector<BlurredSegment *>::iterator it = mbs.begin (); + while (it != mbs.end ()) + { + DigitalStraightSegment *dss = (*it)->getSegment (); + if (dss != NULL) + { + vector<Pt2i> bnd; + dss->getBounds (bnd, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + } + it++; + } + } + else + { + BlurredSegment *bs = detector.getBlurredSegment (1); + if (bs != NULL) + { + DigitalStraightSegment *dss = bs->getSegment (); + if (dss != NULL) + { + vector<Pt2i> bnd; + dss->getBounds (bnd, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + } + } + } + + update (QRect (QPoint (0, 0), QPoint (width, height))); + if (verbose && fixed) displayExtractionResult (); + fixed = false; +} + + +void BSDetectionWidget::extractAll () +{ + bool formerMultiMode = detector.isMultiSelection (); + if (! formerMultiMode) detector.switchMultiSelection (); + augmentedImage = loadedImage; + QPainter painter (&augmentedImage); + + detector.detectAll (); + + // Update auxiliary view if not dragging + if (fixed) + { + if (profileview != NULL) + { + profileview->buildScans (p1, p2); + profileview->scene()->update (); + } + if (accuview != NULL) accuview->scene()->update (); + if (strucview != NULL) strucview->scene()->update (); + } + + vector<BlurredSegment *> mbs = detector.getBlurredSegments (); + instanceOfAll = mbs.size (); + cout << instanceOfAll << " blurred segments detected" << endl; + vector<BlurredSegment *>::iterator it = mbs.begin (); + while (it != mbs.end ()) + { +/* + DigitalStraightSegment *dss = (*it)->getSegment (); + if (dss != NULL) + { + vector<Pt2i> bnd; + dss->getBounds (bnd, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + } +*/ + drawListOfPixels ((*it)->getAllPoints (), Qt::green, painter); + it++; + } + + update (QRect (QPoint (0, 0), QPoint (width, height))); + if (verbose && fixed) displayExtractionResult (); + fixed = false; + if (! formerMultiMode) detector.switchMultiSelection (); +} + + +void BSDetectionWidget::displayNextOfAll () +{ + augmentedImage = loadedImage; + QPainter painter (&augmentedImage); + + vector<BlurredSegment *> mbs = detector.getBlurredSegments (); + if (++instanceOfAll > (int) (mbs.size ())) instanceOfAll = 0; + vector<BlurredSegment *>::iterator it = mbs.begin (); + QColor bsCol = Qt::blue; + int index = 0; + while (index <= instanceOfAll && it != mbs.end ()) + { + if (index++ == instanceOfAll) bsCol = Qt::green; +/* + DigitalStraightSegment *dss = (*it)->getSegment (); + if (dss != NULL) + { + vector<Pt2i> bnd; + dss->getBounds (bnd, 0, 0, width, height); + drawListOfPixels (bnd, Qt::green, painter); + } +*/ + drawListOfPixels ((*it)->getAllPoints (), bsCol, painter); + it++; + } + update (QRect (QPoint (0, 0), QPoint (width, height))); +} + + +void BSDetectionWidget::displayExtractionResult () +{ + int res = detector.result (); + if (res == BSDetector::RESULT_UNDETERMINED) + cout << "Extraction : undetermined." << endl; + else if (res == BSDetector::RESULT_OK) + cout << "Extraction : OK." << endl; + else if (res == BSDetector::RESULT_INITIAL_NO_DETECTION) + cout << "Extraction : no initial detection (bsini == NULL)." << endl; + else if (res == BSDetector::RESULT_INITIAL_TOO_FEW) + cout << "Extraction : two few points at initial detection." << endl; + else if (res == BSDetector::RESULT_INITIAL_TOO_SPARSE) + cout << "Extraction : unsuccessful density test at initial detection." + << endl; + else if (res == BSDetector::RESULT_INITIAL_TOO_MANY_OUTLIERS) + cout << "Extraction : unsuccessful filter test at initial detection." + << endl; + else if (res == BSDetector::RESULT_FINAL_NO_DETECTION) + cout << "Extraction : no final detection (bsini == NULL)." << endl; + else if (res == BSDetector::RESULT_FINAL_TOO_FEW) + cout << "Extraction : two few points at final detection." << endl; + else if (res == BSDetector::RESULT_FINAL_TOO_SPARSE) + cout << "Extraction : unsuccessful density test at final detection." + << endl; + else if (res == BSDetector::RESULT_FINAL_TOO_MANY_OUTLIERS) + cout << "Extraction : unsuccessful filter test at final detection." + << endl; +} + + +void BSDetectionWidget::alternateTest () +{ + if (p1.equals (p2)) + { + cout << "Stroke undefined" << endl; + return; + } + + if (++alternate == 3) alternate = 0; + if (alternate == 0) + { + } + else if (alternate == 1) + { + } + else if (alternate == 2) + { + } +// extract (true); +} + + + +void BSDetectionWidget::performanceTest () +{ +/* + if (p1.equals (p2)) + { + cout << "Stroke undefined" << endl; + return; + } + cout << "Run test" << endl; + clock_t start = clock (); + for (int i = 0; i < 1000; i++) detector.detect (p1, p2); + double diff = (clock () - start) / (double) CLOCKS_PER_SEC; + cout << "Test run : " << diff << endl; + extract (true); +*/ + + cout << "Complete extractions test" << endl; + clock_t start = clock (); + for (int i = 0; i < 100; i++) detector.detectAll (); + double diff = (clock () - start) / (double) CLOCKS_PER_SEC; + cout << "Test run : " << diff << endl; + extract (true); +} + + +void BSDetectionWidget::localTest () +{ + cout << "Run test" << endl; + +/* + p1 = Pt2i (212, 169); + p2 = Pt2i (232, 152); +*/ + + p1 = Pt2i (298, 199); + p2 = Pt2i (279, 173); + + extract (true); + cout << "Test run" << endl; +} diff --git a/Code/Seg/BSTools/bsdetectionwidget.h b/Code/Seg/BSTools/bsdetectionwidget.h new file mode 100755 index 0000000000000000000000000000000000000000..f459a9316ceaa8585f2ccec191880c56dc98d1f0 --- /dev/null +++ b/Code/Seg/BSTools/bsdetectionwidget.h @@ -0,0 +1,270 @@ +#ifndef BS_DETECTION_WIDGET_H +#define BS_DETECTION_WIDGET_H + +#include <QGraphicsView> +#include <QColor> +#include <QImage> +#include <QWidget> +#include <QVector> +#include <fstream> +#include "bsdetector.h" +#include "bsaccumulatorview.h" +#include "bsstructureview.h" +#include "bsprofileview.h" + +using namespace std; + + +/** + * @class BSDetectionWidget bsdetectionwidget.h + * \brief Segment extraction view and controller. + * \author {P. Even and B. Kerautret} + */ +class BSDetectionWidget : public QWidget +{ + Q_OBJECT + + +public: + + /** + * \brief Creates a segment extraction widget. + */ + BSDetectionWidget (QWidget *parent = 0); + + /** + * \brief Deletes the segment extraction widget. + */ + ~BSDetectionWidget (); + + /** + * \brief Opens the image to be processed. + * @param type Name of the image file to open. + * @param type Gradient extraction method. + */ + QSize openImage (const QString &fileName, int type = 0); + + /** + * \brief Builds and returns the image bitmap. + */ + int **getBitmap (const QImage &image); + + /** + * \brief Updates the Qt widget display. + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + */ + + /** + * \brief Saves the augmented image with extraction results. + */ + bool saveAugmentedImage (const QString &fileName, const char *fileFormat); + + /** + * \brief Requires the accumulation window closure. + */ + void closeAccuAnalyzer (); + + /** + * \brief Requires the pixel analysis window closure. + */ + void closePixelAnalyzer (); + + /** + * \brief Requires the profile analysis window closure. + */ + void closeProfileAnalyzer (); + + /** + * \brief Switches the pixel display window on or off. + */ + void switchPixelAnalyzer (); + + /** + * \brief Switches the accumulator display window on or off. + */ + void switchAccuAnalyzer (); + + /** + * \brief Switches the profile display window on or off. + */ + void switchProfileAnalyzer (); + + /** + * \brief Switches the extraction result display on or off. + */ + inline void switchVerbose () { verbose = ! verbose; } + + /** + * \brief Runs a comparative test. + */ + void alternateTest (); + + /** + * \brief Runs a performance test. + * Displays the time spent for 1000 detections under the present stroke. + */ + void performanceTest (); + + /** + * \brief Runs a local test (to be adapted). + */ + void localTest (); + + +public slots: + /** + * \brief Clears the widget drawing. + */ + void clearImage (); + + +protected: + /** + * \brief Updates the widget drawing. + */ + void paintEvent (QPaintEvent *event); + + /** + * \brief Processes mouse press events. + */ + void mousePressEvent (QMouseEvent *event); + + /** + * \brief Processes mouse release events. + */ + void mouseReleaseEvent (QMouseEvent *event); + + /** + * \brief Processes move release events. + */ + void mouseMoveEvent (QMouseEvent *event); + + /** + * \brief Processes key press events. + */ + void keyPressEvent (QKeyEvent *event); + + +private: + + /** Sensitiveness of segment max width parameter. */ + static const int MAX_WIDTH_TUNING; + /** Sensitiveness of detection lacks parameter. */ + static const int DETECTION_LACKS_TUNING; + /** Default value for pen width. */ + static const int DEFAULT_PEN_WIDTH; + + + /** Initial scan end points */ + Pt2i p1, p2; + /** Flag indicating if the stroke is completely defined. */ + bool fixed; + /** Flag indicating whether extraction result should be displayed. */ + bool verbose; + + /** Activation of alternate comparative tests (F8). */ + int alternate; + + /** Index of the last blurred segment displayed in a multi-selection. */ + int instanceOfAll; + + + /** Presently loaded image. */ + QImage loadedImage; + /** Present image augmented with processed data. */ + QImage augmentedImage; + /** Gradient map of the loaded picture. */ + VMap *gMap; + /** Width of the present image. */ + int width; + /** Height of the present image. */ + int height; + /** Blurred segment detector. */ + BSDetector detector; + + /** Aggregation of segment extraction results with initial conditions. */ + struct ExtractedSegment + { + /** Extracted blurred segment. */ + BlurredSegment *bs; + /** Selected strock start point. */ + Pt2i p1; + /** Selected stroke end point. */ + Pt2i p2; + }; + + /** List of registred blurred segments. */ + vector<ExtractedSegment> extractedSegments; + + /** Scanned profile graphics view. */ + BSProfileView *profileview; + /** Filter accumulator view. */ + BSAccumulatorView *accuview; + /** Blurred segment contents view. */ + BSStructureView *strucview; + + + + /** + * \brief Draws a list of points with the given color. + */ + void drawListOfPixels (vector<Pt2i> vectPixels, + QColor color, QPainter &painter); + + /** + * \brief Draws a list of image pixels. + */ + void drawListOfPixels (vector<Pt2i> vectPixels, + QPainter &painter); + + /** + * \brief Draws the line joining two points. + */ + void drawLine (const Pt2i from, const Pt2i to, + QColor color, QPainter &painter); + + /** + * \brief Registers the last extracted blurred segment. + */ + void storeExtractedSegment (); + + /** + * \brief Displays the registred blurred segments. + */ + void displayExtractedSegments (); + + /** + * \brief Clears off the already extracted segments. + */ + void clearExtractedSegments (); + + /** + * \brief Displays the last extraction result. + */ + void displayExtractionResult (); + + /** + * \brief Detects a blurred segment under the selected stroke. + * @param withAllDisplay Indicates if all information about the extraction + * should be displayed. + */ + void extract (bool withAllDisplay); + + /** + * \brief Detects and displays a blurred segment under the selected stroke. + */ + void extract (); + + /** + * \brief Detects and displays all the blurred segment in the picture. + */ + void extractAll (); + + /** + * \brief Highlights the next blurred segment in multi-selection mode. + */ + void displayNextOfAll (); + +}; +#endif diff --git a/Code/Seg/BSTools/bsprofileitem.cpp b/Code/Seg/BSTools/bsprofileitem.cpp new file mode 100755 index 0000000000000000000000000000000000000000..29d183264734f9b7ac1f508c9f9bb531fb19fdc5 --- /dev/null +++ b/Code/Seg/BSTools/bsprofileitem.cpp @@ -0,0 +1,912 @@ +#include <cstdlib> +#include <iostream> +#include <QtGui> +#include "bsprofileitem.h" +#include "directionalscanner.h" + +using namespace std; + + + +const int BSProfileItem::DISPLAY_INTENSITY = 1; +const int BSProfileItem::DISPLAY_GRADIENT = 2; +const int BSProfileItem::DISPLAY_CORRELATION_MEAN_1D = 3; +const int BSProfileItem::DISPLAY_CORRELATION_FULL_2D = 4; +const int BSProfileItem::DISPLAY_CORRELATION_MEAN_2D = 5; +const int BSProfileItem::DISPLAY_MIN = DISPLAY_INTENSITY; +const int BSProfileItem::DISPLAY_MAX = DISPLAY_CORRELATION_MEAN_2D; + +const int BSProfileItem::MIN_SCAN = 8; + + + +BSProfileItem::BSProfileItem () +{ + profileWidth = 400; + profileRatio = 3; + gradientUnRatio = 40; + gradientLow = 0; + + stripeWidth = 200; + stripeMargin = 5; + stripeResol = 4; + + widWidth = profileWidth + stripeWidth + 2 * stripeMargin; + widHeight = 610; + profileLow = (256 - (widHeight / profileRatio)) / 2; + + image = NULL; + imageWidth = 0; + imageHeight = 0; + gMap = NULL; + + displayItem = DISPLAY_INTENSITY; + stripe = 0; + correlWidth = 7; + correlThick = 3; + correlRatio = 4; +} + + +QRectF BSProfileItem::boundingRect () const +{ + return QRectF (0, 0, widWidth, widHeight); +} + + +void BSProfileItem::paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (option); + Q_UNUSED (widget); + + paintStripes (painter); + switch (displayItem) + { + case DISPLAY_INTENSITY : + paintIntensityProfile (painter); + break; + + case DISPLAY_GRADIENT : + paintGradientProfile (painter); + break; + + case DISPLAY_CORRELATION_MEAN_1D : + if (setMeanCorrelationStripes ()) + paintCorrelationProfile (painter); + painter->drawText (100, 20, QString ("W : Correl width = ") + + QString::number (correlWidth)); + painter->drawText (100, 40, QString ("T : Correl thick = ") + + QString::number (correlThick)); + break; + + case DISPLAY_CORRELATION_FULL_2D : + if (setFull2dCorrelationStripes ()) + paintCorrelationProfile (painter); + painter->drawText (100, 20, QString ("W : Correl width = ") + + QString::number (correlWidth)); + painter->drawText (100, 40, QString ("T : Correl thick = ") + + QString::number (correlThick)); + break; + + case DISPLAY_CORRELATION_MEAN_2D : + if (setMean2dCorrelationStripes ()) + paintCorrelationProfile (painter); + painter->drawText (100, 20, QString ("W : Correl width = ") + + QString::number (correlWidth)); + painter->drawText (100, 40, QString ("T : Correl thick = ") + + QString::number (correlThick)); + break; + } +} + + +void BSProfileItem::setImage (QImage *image, VMap *idata) +{ + this->image = image; + this->gMap = idata; + this->imageWidth = image->width (); + this->imageHeight = image->height (); +} + + +void BSProfileItem::buildScans (Pt2i p1, Pt2i p2) +{ + // Updates the central scan end points for parallel display + this->pt1 = p1; + this->pt2 = p2; + + // Resets the profiles + rightscan.clear (); + leftscan.clear (); + rightCorrel.clear (); + leftCorrel.clear (); + rightReCorrel.clear (); + leftReCorrel.clear (); + stripe = 0; + + // Gets a scan iterator + DirectionalScanner *ds = scanp.getScanner (p1, p2, + 0, 0, imageWidth, imageHeight); + + // Extracts the left scan (with central one) + vector<Pt2i> pix; + if (ds->first (pix) < MIN_SCAN) { delete ds; return;} + leftscan.push_back (pix); + + bool leftScanOn = true; + maxStripe = 0; + while (leftScanOn) + { + vector<Pt2i> scan; + if (ds->nextOnLeft (scan) < MIN_SCAN) leftScanOn = false; + else + { + leftscan.push_back (scan); + maxStripe ++; + } + } + + // Extracts the right scans + bool rightScanOn = true; + minStripe = 1; + while (rightScanOn) + { + vector<Pt2i> scan; + if (ds->nextOnRight (scan) < MIN_SCAN) rightScanOn = false; + else + { + rightscan.push_back (scan); + minStripe --; + } + } + + // Frees the scan iterator + delete ds; +} + + +void BSProfileItem::incStripe (int inc) +{ + stripe += inc; + if (stripe > maxStripe) stripe = maxStripe; + else if (stripe < minStripe) stripe = minStripe; +} + + +void BSProfileItem::incCorrelWidth (int down) +{ + if (displayItem <= DISPLAY_CORRELATION_MEAN_2D + && displayItem >= DISPLAY_CORRELATION_MEAN_1D) + { + if (down) + { + if (correlWidth >= 5) correlWidth -= 2; + } + else if (correlWidth <= 19) correlWidth += 2; + } +} + + +void BSProfileItem::incCorrelThick (int down) +{ + if (displayItem <= DISPLAY_CORRELATION_MEAN_2D + && displayItem >= DISPLAY_CORRELATION_MEAN_1D) + { + if (down) + { + if (correlThick >= 3) correlThick -= 2; + } + else if (correlThick <= 17) correlThick += 2; + } +} + + +void BSProfileItem::incCorrelRatio (int down) +{ + if (displayItem <= DISPLAY_CORRELATION_MEAN_2D + && displayItem >= DISPLAY_CORRELATION_MEAN_1D) + { + if (down) + { + if (correlRatio > 1) correlRatio /= 2; + } + else correlRatio *= 2; + } +} + + +void BSProfileItem::toggleDisplay (bool next) +{ + if (displayItem == DISPLAY_CORRELATION_FULL_2D) correlRatio *= 4; + displayItem += (next ? 1 : -1); + if (displayItem > DISPLAY_MAX) displayItem = DISPLAY_MIN; + else if (displayItem < DISPLAY_MIN) displayItem = DISPLAY_MAX; + if (displayItem == DISPLAY_CORRELATION_FULL_2D) correlRatio /= 4; +} + + + + +/** + * Correlation calculation (first release). + * Compares portions of scan bars with a profile extracted at + * the first scan centered on the blurred segment. + */ +void BSProfileItem::setCorrelationStripes (Pt2i p1, Pt2i p2, int segwidth) +{ + leftCorrel.clear (); + rightCorrel.clear (); + leftReCorrel.clear (); + rightReCorrel.clear (); + int correlWidth = segwidth; + if (correlWidth > (int) leftscan.at(0).size ()) + { + cerr << "Can't get correlation stripes" << endl; + return; + } + + // Gets the central index + Pt2i pc ((p1.x () + p2.x ()) / 2, (p1.y () + p2.y ()) / 2); + vector<Pt2i>::iterator it = leftscan.at(0).begin (); + int dist, pos = 0, minPos = 0; + int minDist = (pc.x() - (*it).x()) * (pc.x() - (*it).x()) + + (pc.y() - (*it).y()) * (pc.y() - (*it).y()); + it ++; + while (it != leftscan.at(0).end ()) + { + dist = (pc.x() - (*it).x()) * (pc.x() - (*it).x()) + + (pc.y() - (*it).y()) * (pc.y() - (*it).y()); + if (dist < minDist) + { + minDist = dist; + minPos = pos; + } + it ++; + pos ++; + } + if (minPos - correlWidth / 2 < 0 + || minPos + correlWidth / 2 >= (int) leftscan.size ()) + { + cerr << "Excentred stripe" << endl; + return; + } + + // Gets the central template + int *centralShape = new int[correlWidth]; + for (int i = 0; i < correlWidth; i++) + { + Pt2i pix = leftscan.at(0).at(minPos-correlWidth/2+i); + centralShape[i] = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value (); + } + + // Computes the left correlation stripes + vector <vector <Pt2i> >::iterator scit = leftscan.begin (); + while (scit != leftscan.end ()) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((*scit).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (*scit).size ()) - correlWidth + 1; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + Pt2i pix = (*scit).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + leftCorrel.push_back (corr); + leftReCorrel.push_back (recorr); + scit ++; + } + + // Computes the right correlation stripes + scit = rightscan.begin (); + while (scit != rightscan.end ()) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((*scit).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (*scit).size ()) - correlWidth + 1; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + Pt2i pix = (*scit).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + rightCorrel.push_back (corr); + rightReCorrel.push_back (recorr); + scit ++; + } +} + + +/** Correlation calculation (second release). + * Compares portions of scan bars with a mean profile extracted at + * the center of the first Nth scans (N = correlThick). + */ +bool BSProfileItem::setMeanCorrelationStripes () +{ + leftCorrel.clear (); + rightCorrel.clear (); + leftReCorrel.clear (); + rightReCorrel.clear (); + int minPos = (int) leftscan.at(0).size () / 2; + if (correlWidth > (int) leftscan.at(0).size ()) + { + cerr << "Can't get correlation stripes" << endl; + return false; + } + + // Gets the central template + int *centralShape = new int[correlWidth]; + for (int i = 0; i < correlWidth; i++) + { + centralShape[i] = 0; + for (int j = 0; j < correlThick; j++) + { + Pt2i pix = leftscan.at(j).at(minPos-correlWidth/2+i); + centralShape[i] += ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value (); + } + centralShape[i] /= correlThick; + } + + // Computes the left correlation stripes + vector <vector <Pt2i> >::iterator scit = leftscan.begin (); + while (scit != leftscan.end ()) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((*scit).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (*scit).size ()) - correlWidth + 1; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + Pt2i pix = (*scit).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + leftCorrel.push_back (corr); + leftReCorrel.push_back (recorr); + scit ++; + } + + // Computes the right correlation stripes + scit = rightscan.begin (); + while (scit != rightscan.end ()) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((*scit).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (*scit).size ()) - correlWidth + 1; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + Pt2i pix = (*scit).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + rightCorrel.push_back (corr); + rightReCorrel.push_back (recorr); + scit ++; + } + return true; +} + + +/** Correlation calculation (third release). + * Compares 2D portions of scan bars (N = correlThick) with a 2D profile + * extracted at the center of the first Nth scans (N = correlThick). + */ +bool BSProfileItem::setFull2dCorrelationStripes () +{ + leftCorrel.clear (); + rightCorrel.clear (); + leftReCorrel.clear (); + rightReCorrel.clear (); + if ((int) leftscan.size () <= correlThick) + { + cerr << "Can't get correlation stripes : not enough left scans" << endl; + return false; + } + int minPos = (int) leftscan.at(0).size () / 2; + if (correlWidth > (int) leftscan.at(0).size ()) + { + cerr << "Can't get correlation stripes : scans too narrow" << endl; + return false; + } + + // Gets the central template + int *centralShape = new int[correlThick * correlWidth]; + for (int i = 0; i < correlWidth; i++) + for (int j = 0; j < correlThick; j++) + { + Pt2i pix = leftscan.at(j).at(minPos-correlWidth/2+i); + centralShape[j * correlWidth + i] = ((QColor) image->pixel ( + QPoint (pix.x (), imageHeight - 1 - pix.y ()))).value (); + } + + // Computes the left correlation stripes + for (int i = 0; i < (int) leftscan.size () - correlThick - 2; i++) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((leftscan.at(i)).size ()) >= correlWidth) + for (int j = 0; j < ((int) (leftscan.at(i)).size ()) - correlWidth; j++) + { + int val = 0; + for (int cw = 0; cw < correlThick; cw ++) + for (int k = 0; k < correlWidth; k++) + if ((int) leftscan.at(i+cw).size () > j+k) + { + Pt2i pix = (leftscan.at(i+cw)).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[cw * correlWidth + k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + leftCorrel.push_back (corr); + leftReCorrel.push_back (recorr); + } + + // Computes the right correlation stripes + for (int i = correlThick - 1; i < (int) rightscan.size () - 1; i++) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((rightscan.at(i)).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (rightscan.at(i)).size ()) - correlWidth; j++) + { + int val = 0; + for (int cw = 0; cw < correlThick; cw ++) + for (int k = 0; k < correlWidth; k++) + if ((int) rightscan.at(i-cw).size () > j+k) + { + Pt2i pix = (rightscan.at(i-cw)).at(j+k); + int diff = ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value () + - centralShape[cw * correlWidth + k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + rightCorrel.push_back (corr); + rightReCorrel.push_back (recorr); + } + return true; +} + + + + +/** Correlation calculation (ourth release). + * Compares mean portions of scan bars (N = correlThick) with a mean + * profile extracted at the center of the first Nth scans (N = correlThick). + */ +bool BSProfileItem::setMean2dCorrelationStripes () +{ + leftCorrel.clear (); + rightCorrel.clear (); + leftReCorrel.clear (); + rightReCorrel.clear (); + if ((int) leftscan.size () <= correlThick) + { + cerr << "Can't get correlation stripes : not enough left scans" << endl; + return false; + } + int minPos = (int) leftscan.at(0).size () / 2; + if (correlWidth > (int) leftscan.at(0).size ()) + { + cerr << "Can't get correlation stripes : scans too narrow" << endl; + return false; + } + + // Gets the central template + int *centralShape = new int[correlWidth]; + for (int i = 0; i < correlWidth; i++) + { + centralShape[i] = 0; + for (int j = 0; j < correlThick; j++) + { + Pt2i pix = leftscan.at(j).at(minPos-correlWidth/2+i); + centralShape[i] += ((QColor) image->pixel ( + QPoint (pix.x (), imageHeight - 1 - pix.y ()))).value (); + } + centralShape[i] /= correlThick; + } + + // Computes the left correlation stripes + for (int i = 0; i < (int) leftscan.size () - correlThick - 2; i++) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((leftscan.at(i)).size ()) >= correlWidth) + for (int j = 0; j < ((int) (leftscan.at(i)).size ()) - correlWidth; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + int nbval = 0; + int locval = 0; + for (int cw = 0; cw < correlThick; cw ++) + { + if ((int) leftscan.at(i+cw).size () > j+k) + { + nbval ++; + Pt2i pix = (leftscan.at(i+cw)).at(j+k); + locval += ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value (); + } + } + int diff = locval / nbval - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + leftCorrel.push_back (corr); + leftReCorrel.push_back (recorr); + } + + // Computes the right correlation stripes + for (int i = correlThick - 1; i < (int) rightscan.size () - 1; i++) + { + vector<int> corr; + vector<int> recorr; + if ((int) ((rightscan.at(i)).size ()) >= correlWidth) + { + for (int j = 0; j < ((int) (rightscan.at(i)).size ()) - correlWidth; j++) + { + int val = 0; + for (int k = 0; k < correlWidth; k++) + { + int nbval = 0; + int locval = 0; + for (int cw = 0; cw < correlThick; cw ++) + { + if ((int) rightscan.at(i-cw).size () > j+k) + { + nbval ++; + Pt2i pix = (rightscan.at(i-cw)).at(j+k); + locval += ((QColor) image->pixel (QPoint (pix.x (), + imageHeight - 1 - pix.y ()))).value (); + } + } + int diff = locval / nbval - centralShape[k]; + val += (diff < 0 ? - diff : diff); + } + corr.push_back (val); + recorr.push_back ((int) val); + } + } + rightCorrel.push_back (corr); + rightReCorrel.push_back (recorr); + } + return true; +} + + +void BSProfileItem::getLocalMinimaIndices (vector<int> &indices, + const vector<int> &signal) const +{ + int ng = signal.size (); + int offset = 0; + bool up = true; + + // Gets the first distinct value from start + while (offset < ng - 1 && signal.at (offset) == signal.at (0)) + { + if (signal.at (offset) - signal.at (offset + 1) < 0) + { + up = true; + break; + } + if (signal.at (offset) - signal.at (offset + 1) > 0) + { + up = false; + break; + } + offset++; + } + + for(int i = offset; i < ng - 1; i++) + { + if (up) + { + if ((signal.at (i + 1) - signal.at (i)) < 0) up = false; + } + else + { + if (signal.at (i + 1) - signal.at (i) > 0) + { + up = true; + int k = i; + while (signal.at (k) == signal.at (i)) k--; + indices.push_back (k + 1 + (i - k - 1) / 2); + } + } + } +} + + + + +void BSProfileItem::paintStripes (QPainter *painter) +{ + int lx = 80; + int cx, cy = widHeight / 2; + vector <vector <Pt2i> >::iterator bigit; + + if (rightscan.size ()) + lx = profileWidth + stripeMargin + stripeWidth / 2 + - stripeResol / 2 - rightscan.at(0).size () * stripeResol / 2; + else if (leftscan.size ()) + lx = profileWidth + stripeMargin + stripeWidth / 2 + - stripeResol / 2 - leftscan.at(0).size () * stripeResol / 2; + painter->setPen (QPen (Qt::red, 2)); + painter->drawRect (profileWidth + stripeMargin - 1, stripeMargin - 1, + stripeWidth + 2, widHeight - 2 * stripeMargin + 2); + + // Lower part (right side) + if (rightscan.size ()) + { + bigit = rightscan.begin (); + while (cy <= widWidth - stripeMargin - stripeResol + && bigit != rightscan.end ()) + { + cx = lx; + vector<Pt2i> scan = *bigit; + vector<Pt2i>::iterator it = scan.begin (); + while (cx < widWidth - stripeMargin - stripeResol && it != scan.end ()) + { + if (cx >= profileWidth + stripeMargin) + painter->fillRect (cx, cy, stripeResol, stripeResol, + QBrush (image->pixel ((*it).x (), imageHeight - 1 - (*it).y ()))); + it ++; + cx += stripeResol; + } + bigit ++; + cy += stripeResol; + } + } + + // Upper part (left side) + if (leftscan.size ()) + { + cy = widHeight / 2 - 2 * stripeResol; + bigit = leftscan.begin (); + while (cy >= 5 && bigit != leftscan.end ()) + { + cx = lx; + vector<Pt2i> scan = *bigit; + vector<Pt2i>::iterator it = scan.begin (); + while (cx < widHeight - stripeMargin - stripeResol && it != scan.end ()) + { + if (cx >= profileWidth + stripeMargin) + painter->fillRect (cx, cy, stripeResol, stripeResol, + QBrush (image->pixel ((*it).x (), imageHeight - 1 - (*it).y ()))); + it ++; + cx += stripeResol; + } + bigit ++; + cy -= stripeResol; + } + } + + // Enclosing the central scan + if (stripe >= 0) + cy = widHeight / 2 - 1 - 2 * stripeResol - stripe * stripeResol; + else cy = widHeight / 2 - 1 - (1 + stripe) * stripeResol; + painter->setPen (QPen (Qt::green, 2)); + painter->drawRect (profileWidth + stripeMargin - 1, cy, + stripeWidth + 2, stripeResol + 2); +} + + +void BSProfileItem::paintIntensityProfile (QPainter *painter) +{ + if (rightscan.size () || leftscan.size ()) + { + vector<Pt2i> scan; + if (stripe >= 0) scan = leftscan.at (stripe); + else scan = rightscan.at (- stripe - 1); + int h, cx = 0, w = profileWidth / (leftscan.at(0)).size (); + vector<Pt2i>::iterator it = scan.begin (); + while (it != scan.end ()) + { + if ((*it).x () < 0 || (*it).x () >= imageWidth + || (*it).y () < 0 || (*it).y () >= imageHeight) + cerr << "OUT OF IMAGE BOUNDS : (" << (*it).x () << "," + << imageHeight - 1 - (*it).y () << ")" << endl; + else + { + h = ((QColor) image->pixel (QPoint ((*it).x (), + imageHeight - 1 - (*it).y ()))).value (); + h = (h - profileLow) * profileRatio; + if (h < 0) h = 0; + else if (h > widHeight) h = widHeight; + if (h) painter->fillRect (cx, widHeight - h, w, h, QBrush (Qt::blue)); + it ++; + cx += w; + } + } + + painter->setPen (QPen (Qt::black, 2)); + scan = leftscan.at (0); + it = scan.begin (); + int prevh = ((QColor) image->pixel (QPoint ((*it).x (), + imageHeight - 1 - (*it).y ()))).value (); + prevh = (prevh - profileLow) * profileRatio; + if (prevh < 0) prevh = 0; + else if (prevh > widWidth) prevh = widWidth; + cx = 0; + while (it != scan.end ()) + { + h = ((QColor) image->pixel (QPoint ((*it).x (), + imageHeight - 1 - (*it).y ()))).value (); + h = (h - profileLow) * profileRatio; + if (h < 0) h = 0; + else if (h > widWidth) h = widWidth; + painter->drawLine (cx, widWidth - prevh, cx, widWidth - h); + painter->drawLine (cx, widWidth - h, cx + w, widWidth - h); + prevh = h; + it ++; + cx += w; + } + } +} + + +void BSProfileItem::paintGradientProfile (QPainter *painter) +{ + if (rightscan.size () || leftscan.size ()) + { + vector<Pt2i> scan; + if (stripe >= 0) scan = leftscan.at (stripe); + else scan = rightscan.at (- stripe - 1); + int cx = 0, w = profileWidth / (leftscan.at(0)).size (); + int h, prevh; + vector<Pt2i>::iterator it = scan.begin (); + while (it != scan.end ()) + { + if ((*it).x () < 0 || (*it).x () >= imageWidth + || (*it).y () < 0 || (*it).y () >= imageHeight) + cerr << "OUT OF IMAGE BOUNDS : (" << (*it).x () << "," + << imageHeight - 1 - (*it).y () << ")" << endl; + else + { + h = (gMap->sqNorm ((*it).x (), (*it).y ()) - gradientLow) + / gradientUnRatio; + if (h < 0) h = 0; + else if (h > widHeight) h = widHeight; + if (h) painter->fillRect (cx, widHeight - h, w, h, QBrush (Qt::blue)); + it ++; + cx += w; + } + } + + painter->setPen (QPen (Qt::black, 2)); + scan = leftscan.at (0); + it = scan.begin (); + prevh = (gMap->sqNorm ((*it).x (), (*it).y ()) - gradientLow) + / gradientUnRatio; + if (prevh < 0) prevh = 0; + else if (prevh > widWidth) prevh = widWidth; + cx = 0; + while (it != scan.end ()) + { + h = (gMap->sqNorm ((*it).x (), (*it).y ()) - gradientLow) + / gradientUnRatio; + if (h < 0) h = 0; + else if (h > widWidth) h = widWidth; + painter->drawLine (cx, widWidth - prevh, cx, widWidth - h); + painter->drawLine (cx, widWidth - h, cx + w, widWidth - h); + prevh = h; + it ++; + cx += w; + } + } +} + + + +void BSProfileItem::paintCorrelationProfile (QPainter *painter) +{ + if (rightCorrel.size () || leftCorrel.size ()) + { + Pt2i gr; + vector<int> scan; + vector<int> rescan; + if (stripe >= 0) + { + scan = leftCorrel.at (stripe); + rescan = leftReCorrel.at (stripe); + } + else + { + scan = rightCorrel.at (- stripe - 1); + rescan = rightReCorrel.at (- stripe - 1); + } + + int h, cx = 0, w = profileWidth / (leftscan.at(0)).size (); + vector<int>::iterator it = scan.begin (); + while (it != scan.end ()) + { + h = (*it) * correlRatio; + if (h > widHeight) h = widHeight; + if (h) painter->fillRect (cx, widHeight - h, w, h, QBrush (Qt::blue)); + it ++; + cx += w; + } + + vector<int> locs; + getLocalMinimaIndices (locs, rescan); + it = locs.begin (); + while (it != locs.end ()) + { + h = scan.at(*it) * correlRatio; + painter->fillRect ((*it) * w + 4, widHeight - h - 14, w - 8, 10, + QBrush (Qt::red)); + it ++; + } + + painter->setPen (QPen (Qt::black, 2)); + scan = leftCorrel.at (0); + it = scan.begin (); + int prevh = (*it) * correlRatio; + if (prevh > widWidth) prevh = widWidth; + cx = 0; + while (it != scan.end ()) + { + h = (*it) * correlRatio; + if (h > widWidth) h = widWidth; + painter->drawLine (cx, widWidth - prevh, cx, widWidth - h); + painter->drawLine (cx, widWidth - h, cx + w, widWidth - h); + prevh = h; + it ++; + cx += w; + } + } +} diff --git a/Code/Seg/BSTools/bsprofileitem.h b/Code/Seg/BSTools/bsprofileitem.h new file mode 100755 index 0000000000000000000000000000000000000000..3ca04bca9178bc1c8394b0757599e293f51b8410 --- /dev/null +++ b/Code/Seg/BSTools/bsprofileitem.h @@ -0,0 +1,249 @@ +#ifndef BS_PROFILE_ITEM_H +#define BS_PROFILE_ITEM_H + +#include <QGraphicsItem> +#include <QImage> +#include <QKeyEvent> +#include "vmap.h" +#include "scannerprovider.h" + + +/** + * @class BSProfileItem bsprofileitem.h + * \brief Scan intensity profile view and controller. + * \author {P. Even} + */ +class BSProfileItem : public QGraphicsItem +{ + +public: + + + /** + * \brief Creates a profile analysis widget. + */ + BSProfileItem (); + + /** + * \brief Declares the image to be analysed. + */ + void setImage (QImage *image, VMap *idata); + + /** + * \brief Sets the image scan area from an initial scan. + * The initial scan is a straight segment from p1 to p2. + */ + void buildScans (Pt2i p1, Pt2i p2); + + /** + * \brief Returns the widget size. + * Nominally the image size. + */ + QRectF boundingRect () const; + + /** + * \brief Updates the widget display. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + /** + * \brief Returns the displayed information title. + */ + inline QString itemTitle () const + { + if (displayItem == DISPLAY_INTENSITY) + return ("Intensity profiles"); + else if (displayItem == DISPLAY_GRADIENT) + return ("Gradient profiles"); + else if (displayItem == DISPLAY_CORRELATION_MEAN_1D) + return ("Correlation profiles (mean 1D)"); + else if (displayItem == DISPLAY_CORRELATION_FULL_2D) + return ("Correlation profiles (full 2D)"); + else if (displayItem == DISPLAY_CORRELATION_MEAN_2D) + return ("Correlation profiles (mean 2D)"); + else return ("No profile"); + } + + /** + * \brief Increments the current stripe index. + * @param inc Direction (1 for leftwards, -1 for rightwards) + */ + void incStripe (int inc); + + /** + * \brief Increments or decrements the scan correlation width. + * @param down Direction (1 to increment, -1 to decrement) + */ + void incCorrelWidth (int down); + + /** + * \brief Increments or decrements the scan correlation thickness. + * @param down Direction (1 to increment, -1 to decrement) + */ + void incCorrelThick (int down); + + /** + * \brief Increments or decrements the scan correlation ratio. + * @param down Direction (1 to increment, -1 to decrement) + */ + void incCorrelRatio (int down); + + /** + * \brief Toggles the displayed information. + * @param next Get next information if true, previous on otherwise. + */ + void toggleDisplay (bool next); + + +protected: + +private: + + /** Available information : intensity profiles. */ + static const int DISPLAY_INTENSITY; + /** Available information : gradient profiles. */ + static const int DISPLAY_GRADIENT; + /** Available information : 1D mean correlation profiles. */ + static const int DISPLAY_CORRELATION_MEAN_1D; + /** Available information : 2D full correlation profiles. */ + static const int DISPLAY_CORRELATION_FULL_2D; + /** Available information : 1D full correlation profiles. */ + static const int DISPLAY_CORRELATION_MEAN_2D; + /** Number of the first information. */ + static const int DISPLAY_MIN; + /** Number of the last information. */ + static const int DISPLAY_MAX; + + /** Analysis widget height. */ + int widHeight; + /** Analysis widget width. */ + int widWidth; + + /** Profile area width. */ + int profileWidth; + /** Intensity profile vertical zoom factor. */ + int profileRatio; + /** Intensity profile lowest visible value. */ + int profileLow; + + /** Gradient profile vertical unzoom factor. */ + int gradientUnRatio; + /** Gradient profile lowest visible value. */ + int gradientLow; + + /** Correlation profile vertical zoom factor (power of 2). */ + int correlRatio; + /** Correlation profile width. */ + int correlWidth; + /** Correlation profile scan thickness. */ + int correlThick; + + /** Stripe area width. */ + int stripeWidth; + /** Stripe area margin width. */ + int stripeMargin; + /** Stripe points zoom factor. */ + int stripeResol; + + /** Analysed image. */ + QImage *image; + /* Analysed image width. */ + int imageWidth; + /** Analyzed image height. */ + int imageHeight; + /** Gradient map. */ + VMap *gMap; + + /** Central scan start point. */ + Pt2i pt1; + /** Central scan start point. */ + Pt2i pt2; + /** Central and left scans. */ + vector <vector <Pt2i> > leftscan; + /** Right scans. */ + vector <vector <Pt2i> > rightscan; + /** Minimum scan length allowed. */ + static const int MIN_SCAN; + + // Just for local minima extraction. + vector <vector <int> > leftCorrel; + vector <vector <int> > rightCorrel; + vector <vector <int> > leftReCorrel; + vector <vector <int> > rightReCorrel; + + /** Displayed information (intensity, gradient, correlation). */ + int displayItem; + /** Current stripe index. */ + int stripe; + /** Min stripe index (right scan size). */ + int minStripe; + /** Max stripe index (left scan size). */ + int maxStripe; + + /** Scanner provider (that selects the appropriate octant) */ + ScannerProvider scanp; + + + /** + * \brief Correlation calculation (first release). + * \author {B. Kerautret} + * Compares portions of scan bars with a profile extracted at + * the first scan centered on the blurred segment. + */ + void setCorrelationStripes (Pt2i p1, Pt2i p2, int segwidth); + + /** + * \brief Correlation calculation (second release). + * \author {B. Kerautret} + * Compares portions of scan bars with a mean profile extracted at + * the center of the first Nth scans (N = correlThick). + */ + bool setMeanCorrelationStripes (); + + /** + * \brief Correlation calculation (third release). + * \author {B. Kerautret} + * Compares 2D portions of scan bars (N = correlThick) with a 2D profile + * extracted at the center of the first Nth scans (N = correlThick). + */ + bool setFull2dCorrelationStripes (); + + /** + * \brief Correlation calculation (fourth release). + * \author {B. Kerautret} + * Compares mean portions of scan bars (N = correlThick) with a mean + * profile extracted at the center of the first Nth scans (N = correlThick). + */ + bool setMean2dCorrelationStripes (); + + /** + * \brief Extract local minima from given signal. + * @param indices List of indices of the signal local minima. + * @param signal Input signal. + */ + void getLocalMinimaIndices (vector<int> &indices, + const vector<int> &signal) const; + + /** + * \brief Draws the scan strip. + */ + void paintStripes (QPainter *painter); + + /** + * \brief Draws the intensity profile. + */ + void paintIntensityProfile (QPainter *painter); + + /** + * \brief Draws the gradient profile. + */ + void paintGradientProfile (QPainter *painter); + + /** + * \brief Draws the correlation profile. + */ + void paintCorrelationProfile (QPainter *painter); +}; + +#endif diff --git a/Code/Seg/BSTools/bsprofileview.cpp b/Code/Seg/BSTools/bsprofileview.cpp new file mode 100755 index 0000000000000000000000000000000000000000..e20f8b7ab5233a40bd34160b306c2940e7db6c65 --- /dev/null +++ b/Code/Seg/BSTools/bsprofileview.cpp @@ -0,0 +1,87 @@ +#include <QtGui> +#include <iostream> +#include <cstdlib> +#include "bsprofileview.h" + +using namespace std; + + + +BSProfileView::BSProfileView () +{ + // CAUTION : don't activate antialiasing here !!! + setBackgroundBrush (QBrush (Qt::white)); + setScene (new QGraphicsScene (0, 0, 610, 610)); + prof = new BSProfileItem (); + scene()->addItem (prof); + setWindowTitle (prof->itemTitle ()); + resize (QSize (616, 616)); +} + + +BSProfileView::~BSProfileView () +{ + scene()->removeItem (prof); + delete prof; +} + + +void BSProfileView::paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (option); + Q_UNUSED (widget); + Q_UNUSED (painter); +} + + +void BSProfileView::setImage (QImage *image, VMap *idata) +{ + prof->setImage (image, idata); +} + + +void BSProfileView::buildScans (Pt2i p1, Pt2i p2) +{ + prof->buildScans (p1, p2); +} + + +bool BSProfileView::processKeyEvent (QKeyEvent *event) +{ + switch (event->key ()) + { + case Qt::Key_W : // Width of correlation measure + prof->incCorrelWidth (event->modifiers () & Qt::ShiftModifier); + prof->update (); + break; + + case Qt::Key_T : // Thickness of correlation measure + prof->incCorrelThick (event->modifiers () & Qt::ShiftModifier); + prof->update (); + break; + + case Qt::Key_R : // Ratio of correlation measure + prof->incCorrelRatio (event->modifiers () & Qt::ShiftModifier); + prof->update (); + break; + + case Qt::Key_I : + prof->toggleDisplay ((event->modifiers () & Qt::ShiftModifier) == 0); + setWindowTitle (prof->itemTitle ()); + prof->update (); + break; + + case Qt::Key_Up : + prof->incStripe (1); + prof->update (); + break; + + case Qt::Key_Down : + prof->incStripe (-1); + prof->update (); + break; + } + return false; +} diff --git a/Code/Seg/BSTools/bsprofileview.h b/Code/Seg/BSTools/bsprofileview.h new file mode 100755 index 0000000000000000000000000000000000000000..462648cf02106121b3205c7344ed76b32466fded --- /dev/null +++ b/Code/Seg/BSTools/bsprofileview.h @@ -0,0 +1,58 @@ +#ifndef BS_PROFILE_VIEW_H +#define BS_PROFILE_VIEW_H + +#include <QGraphicsView> +#include "bsprofileitem.h" + + +/** + * @class BSProfileView bsprofileview.h + * \brief A Qt window containing informations about scanned profiles. + * \author {P. Even} + */ +class BSProfileView : public QGraphicsView +{ + +public: + + /** + * \brief Creates a profile analysis window. + */ + BSProfileView (); + + /** + * \brief Deletes the profile analysis window. + */ + ~BSProfileView (); + + /** + * \brief Updates the profile analysis window display. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + /** + * \brief Declares the image to be analysed. + */ + void setImage (QImage *image, VMap *idata); + + /** + * \brief Sets the image scan area from an initial scan. + * The initial scan is a straight segment from p1 to p2. + */ + void buildScans (Pt2i p1, Pt2i p2); + + /** + * \brief Processes key pressed events. + */ + bool processKeyEvent (QKeyEvent *event); + + +private: + + /** Profile analysis widget. */ + BSProfileItem *prof; + +}; + +#endif diff --git a/Code/Seg/BSTools/bsstructureitem.cpp b/Code/Seg/BSTools/bsstructureitem.cpp new file mode 100755 index 0000000000000000000000000000000000000000..c4cf2947f1fc77e56b26d6d0d47ae733349b0745 --- /dev/null +++ b/Code/Seg/BSTools/bsstructureitem.cpp @@ -0,0 +1,326 @@ +#include <QtGui> +#include <iostream> +#include "bsstructureitem.h" + +using namespace std; + + +const int BSStructureItem::DISPLAY_FINAL_BLURRED_SEGMENT = 1; +const int BSStructureItem::DISPLAY_FINAL_CONNECTED_COMPONENTS = 2; +const int BSStructureItem::DISPLAY_FINAL_SCANS_AND_FILTER = 3; +const int BSStructureItem::DISPLAY_INITIAL_BLURRED_SEGMENT = 4; +const int BSStructureItem::DISPLAY_INITIAL_CONNECTED_COMPONENTS = 5; +const int BSStructureItem::DISPLAY_INITIAL_SCANS_AND_FILTER = 6; +const int BSStructureItem::DISPLAY_MIN = DISPLAY_FINAL_BLURRED_SEGMENT; +const int BSStructureItem::DISPLAY_MAX = DISPLAY_INITIAL_SCANS_AND_FILTER; + +const int BSStructureItem::DEFAULT_PEN_WIDTH = 1; +const int BSStructureItem::LEFT_MARGIN = 16; +const int BSStructureItem::TEXT_HEIGHT = 16; + +const int BSStructureItem::MAX_ZOOM = 16; + + + +BSStructureItem::BSStructureItem (int width, int height, const QImage *im, + BSDetector *detector) +{ + w = width; + h = height; + zoom = 1; + focx = 0; + focy = 0; + det = detector; + displayItem = DISPLAY_MIN; + displayScanLines = false; + this->im = im; + verbose = true; + infoPen = QPen (Qt::red, DEFAULT_PEN_WIDTH, Qt::SolidLine, + Qt::RoundCap, Qt::RoundJoin); +} + + +BSStructureItem::~BSStructureItem () +{ +} + + +void BSStructureItem::zoomIn () +{ + if (zoom < MAX_ZOOM) zoom *= 2; +} + + +void BSStructureItem::zoomOut () +{ + if (zoom > 1) + { + int w2 = w / 2; + int h2 = h / 2; + zoom /= 2; + if (focx < w2 / zoom - w2) focx = w2 / zoom - w2; + if (focx > w2 - w2 / zoom) focx = w2 - w2 / zoom; + if (focy < h2 / zoom - h2) focy = h2 / zoom - h2; + if (focy > h2 - h2 / zoom) focy = h2 - h2 / zoom; + } +} + + +void BSStructureItem::shift (int dx, int dy) +{ + int w2 = w / 2; + int h2 = h / 2; + focx += zoom * dx; + focy += zoom * dy; + if (focx < w2 / zoom - w2) focx = w2 / zoom - w2; + if (focx > w2 - w2 / zoom) focx = w2 - w2 / zoom; + if (focy < h2 / zoom - h2) focy = h2 / zoom - h2; + if (focy > h2 - h2 / zoom) focy = h2 - h2 / zoom; +} + + +QRectF BSStructureItem::boundingRect () const +{ + return QRectF (0, 0, w - 1, h - 1); // ZZZ +} + + +void BSStructureItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (option); + Q_UNUSED (widget); + + if (displayItem == DISPLAY_FINAL_BLURRED_SEGMENT) + paintBlurredSegment (painter, 1); + else if (displayItem == DISPLAY_FINAL_CONNECTED_COMPONENTS) + paintConnectedComponents (painter, 1); + else if (displayItem == DISPLAY_FINAL_SCANS_AND_FILTER) + paintScansAndFilter (painter, 1); + if (displayItem == DISPLAY_INITIAL_BLURRED_SEGMENT) + paintBlurredSegment (painter, 0); + else if (displayItem == DISPLAY_INITIAL_CONNECTED_COMPONENTS) + paintConnectedComponents (painter, 0); + else if (displayItem == DISPLAY_INITIAL_SCANS_AND_FILTER) + paintScansAndFilter (painter, 0); +} + + +void BSStructureItem::paintBlurredSegment (QPainter *painter, int step) +{ + BlurredSegment *bs = det->getBlurredSegment (step); + if (bs != NULL) + { + DigitalStraightSegment *dss = bs->getSegment (); + if (dss != NULL) + { + vector<Pt2i> bnd; + dss->getBounds (bnd, 0, 0, w, h); + paintPixels (painter, bnd, Qt::green); + } + paintPixels (painter, bs->getAllPoints (), Qt::blue); + + if (verbose) + { + initText (painter); + if (dss != NULL) + addText (painter, QString ("Segment width = ") + + QString::number (dss->width ()) + QString ("/") + + QString::number (dss->period ()) + QString (" = ") + + QString::number (dss->width () / (double) dss->period ())); + else addText (painter, QString ("Segment width = 0")); + addText (painter, QString ("W : input max width = ") + + QString::number (det->getInputMaxWidth ())); + addText (painter, QString ("L : output BS min size = ") + + QString::number (det->getBSminSize ())); + addText (painter, QString ("H : pixel lack tolerence = ") + + QString::number (det->getPixelLackTolerence ())); + if (step == 0) + { + addText (painter, QString ("D : extension limit = ") + + QString::number (det->initialDetectionMaxExtent ())); + addText (painter, QString ("P : Prefiltering ") + + (det->isFiltering (step) ? + (QString ("on : ") + + QString::number (det->prefilteringInputSize ()) + + QString (" -> ") + + QString::number (det->prefilteringOutputSize ()) + + QString (" pixels")) : + QString ("off"))); + } + } + } +} + + +void BSStructureItem::paintConnectedComponents (QPainter *painter, int step) +{ + BlurredSegment *bs = det->getBlurredSegment (step); + if (bs != NULL) + { + QColor cols[] = {Qt::blue, Qt::red, Qt::green}; + int col = 0; + vector < vector <Pt2i> > cc = bs->getConnectedComponents (); + vector < vector <Pt2i> >::const_iterator it = cc.begin (); + while (it != cc.end ()) + { + paintPixels (painter, *it, cols[col]); + if (++col == 3) col = 0; + it++; + } + } + + if (verbose) + { + initText (painter); + int ccs = det->connectedComponentMinSize (); + if (bs != NULL) + { + int bsccp = bs->countOfConnectedPoints (ccs); + int bssize = bs->getAllPoints().size (); + addText (painter, QString ("G connectivity constraint ") + + (det->isSetConnectivityConstraint () ? + QString ("on") : QString ("off"))); + addText (painter, QString ("C connected components min size = ") + + QString::number (ccs) + QString (" points")); + addText (painter, QString::number (bssize) + QString (" points")); + addText (painter, QString::number (bs->countOfConnectedComponents ()) + + QString (" connected components")); + addText (painter, QString::number (bs->countOfConnectedPoints ()) + + QString (" connected points")); + addText (painter, QString::number (bs->countOfConnectedComponents (ccs)) + + QString (" connected components with min size ") + + QString::number (ccs)); + addText (painter, QString::number (bsccp) + + QString (" connected points with min size ") + + QString::number (ccs)); + if (bsccp < bssize / 2) + addText (painter, QString ("BS too sparse !")); + } + } +} + + +void BSStructureItem::paintScansAndFilter (QPainter *painter, int step) +{ + if (det->scanRecordOn (step)) + { + if (displayScanLines) + { + QColor col[] = {Qt::blue, Qt::red, Qt::green}; + int i = 0; + vector < vector <Pt2i> > scns = det->getScans (step); + if (scns.size () > 0) + { + vector <vector <Pt2i> >::const_iterator it = scns.begin (); + paintPixel (painter, it->front (), col[2]); + paintPixel (painter, it->back (), col[2]); + it++; // Displays only the bounds of the central scan + while (it != scns.end ()) + { + paintPixels (painter, *it, col[i]); + it++; + if (++i == 3) i = 0; + } + } + } + else + { + // Displays scan bounds + paintPixels (painter, det->getScanBound1 (step), Qt::blue); + paintPixels (painter, det->getScanBound2 (step), Qt::blue); + + // Displays filter + if (det->isFiltering (step)) + { + paintPixels (painter, det->getAccepted (step), Qt::green); + paintPixels (painter, det->getRejected (step), Qt::red); + BlurredSegment *bs = det->getBlurredSegment (step); + if (bs != NULL) paintPixels (painter, bs->getStartPt (), Qt::yellow); + } + } + } + + if (verbose) + { + initText (painter); + addText (painter, + QString ("S : ") + (det->dynamicScansOn () ? + QString ("dynamic scans") : QString ("static scans"))); + addText (painter, + QString ("O : ") + (det->orthoScansOn () ? + QString ("vert/horiz scans") : QString ("directional scans"))); + } +} + + +void BSStructureItem::toggleDisplay (bool next) +{ + displayItem += (next ? 1 : -1); + if (displayItem > DISPLAY_MAX) displayItem = DISPLAY_MIN; + else if (displayItem < DISPLAY_MIN) displayItem = DISPLAY_MAX; + det->setScanRecord (1, displayItem == DISPLAY_FINAL_SCANS_AND_FILTER); + det->setScanRecord (0, displayItem == DISPLAY_INITIAL_SCANS_AND_FILTER); +} + + +void BSStructureItem::paintPixels (QPainter *painter, + const vector<Pt2i> &pix, const QColor col) +{ + QBrush brush (col); + vector<Pt2i>::const_iterator iter = pix.begin (); + while (iter != pix.end ()) + { + Pt2i p = *iter; + int dx = w / 2 + focx - p.x (); + int dy = h / 2 - focy - p.y (); + painter->fillRect (w / 2 - zoom * dx, + (h / 2 + zoom * dy), + zoom, zoom, brush); + iter++; + } +} + + +void BSStructureItem::paintPixels (QPainter *painter, + const vector<Pt2i> &pix) +{ + vector<Pt2i>::const_iterator iter = pix.begin (); + while (iter != pix.end ()) + { + Pt2i p = *iter; + int dx = w / 2 + focx - p.x (); + int dy = h / 2 - focy - p.y (); + QBrush brush (im->pixel (p.x (), im->height () - 1 - p.y ())); + painter->fillRect (w / 2 - zoom * dx, + (h / 2 + zoom * dy), + zoom, zoom, brush); + iter++; + } +} + + +void BSStructureItem::paintPixel (QPainter *painter, + const Pt2i &pix, const QColor col) +{ + QBrush brush (col); + int dx = w / 2 + focx - pix.x (); + int dy = h / 2 - focy - pix.y (); + painter->fillRect (w / 2 - zoom * dx, (h / 2 + zoom * dy), // dec 1 + zoom, zoom, brush); +} + + +void BSStructureItem::initText (QPainter *painter) +{ + painter->setPen (infoPen); + textOffset = 0; +} + + +void BSStructureItem::addText (QPainter *painter, const QString &text) +{ + textOffset += TEXT_HEIGHT; + painter->drawText (LEFT_MARGIN, textOffset, text); +} diff --git a/Code/Seg/BSTools/bsstructureitem.h b/Code/Seg/BSTools/bsstructureitem.h new file mode 100755 index 0000000000000000000000000000000000000000..d8ea16f85eed6dbab71cc68816e19a3ac14a624b --- /dev/null +++ b/Code/Seg/BSTools/bsstructureitem.h @@ -0,0 +1,218 @@ +#ifndef BS_STRUCTURE_ITEM_H +#define BS_STRUCTURE_ITEM_H + +#include <QGraphicsItem> +#include <vector> +#include "bsdetector.h" + + +class BSStructureItem : public QGraphicsItem +{ +public: + + /** + * \brief Creates a pixel analysis grid. + */ + BSStructureItem (int width, int height, + const QImage *im, BSDetector *detector); + + /** + * \brief Deletes the pixel analysis grid. + */ + ~BSStructureItem (); + + /** + * \brief Return the zoom factor. + */ + inline int zoomFactor () const { return zoom; } + + /** + * \brief Zooms the grid in. + */ + void zoomIn (); + + /** + * \brief Zooms the grid out. + */ + void zoomOut (); + + /** + * \brief Return the focus point abscissae. + */ + inline int focusX () const { return focx; } + + /** + * \brief Return the focus point ordinate. + */ + inline int focusY () const { return focy; } + + /** + * \brief Shifts the grid. + * @param dx shift X value. + * @param dy shift Y value. + */ + void shift (int dx, int dy); + + /** + * \brief Toggles the displayed information. + * @param next Get next information if true, previous on otherwise. + */ + void toggleDisplay (bool next); + + /** + * \brief Switches on or off the information text display modality. + */ + inline void switchInfoDisplay () { verbose = ! verbose; } + + /** + * \brief Switches on or off the scan line display modality. + * @param next Get next information if true, previous on otherwise. + */ + inline void switchScanDisplay () { displayScanLines = ! displayScanLines; } + + /** + * \brief Returns the displayed information title. + */ + inline QString itemTitle () const + { + if (displayItem == DISPLAY_FINAL_BLURRED_SEGMENT) + return ("Final blurred segment"); + else if (displayItem == DISPLAY_FINAL_CONNECTED_COMPONENTS) + return ("Final connected components"); + else if (displayItem == DISPLAY_FINAL_SCANS_AND_FILTER) + return ("Final scans and filter"); + else if (displayItem == DISPLAY_INITIAL_BLURRED_SEGMENT) + return ("Initial blurred segment"); + else if (displayItem == DISPLAY_INITIAL_CONNECTED_COMPONENTS) + return ("Initial connected components"); + else if (displayItem == DISPLAY_INITIAL_SCANS_AND_FILTER) + return ("Initial scans and filter"); + else return ("No info"); + } + + /** + * \brief Returns the grid area. + */ + QRectF boundingRect () const; + + /** + * \brief Redraws the pixel analysis grid. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + +private: + + /** Available information : final blurred segment pixels and bounds. */ + static const int DISPLAY_FINAL_BLURRED_SEGMENT; + /** Available information : final connected components. */ + static const int DISPLAY_FINAL_CONNECTED_COMPONENTS; + /** Available information : final scans and filter output. */ + static const int DISPLAY_FINAL_SCANS_AND_FILTER; + /** Available information : initial blurred segment points and bounds. */ + static const int DISPLAY_INITIAL_BLURRED_SEGMENT; + /** Available information : initial connected components. */ + static const int DISPLAY_INITIAL_CONNECTED_COMPONENTS; + /** Available information : initial scans and filter output. */ + static const int DISPLAY_INITIAL_SCANS_AND_FILTER; + /** Number of the first information. */ + static const int DISPLAY_MIN; + /** Number of the last information. */ + static const int DISPLAY_MAX; + + /** Default value for pen width. */ + static const int DEFAULT_PEN_WIDTH; + /** Left margin for information text. */ + static const int LEFT_MARGIN; + /** Information text height. */ + static const int TEXT_HEIGHT; + + const QImage *im; + /** Grid width. */ + int w; + /** Grid height. */ + int h; + /** Zoom factor. */ + static const int MAX_ZOOM; + /** Zoom factor. */ + int zoom; + /** Focus point abscissae. */ + int focx; + /** Focus point ordinate. */ + int focy; + /** Segment detector. */ + BSDetector *det; + /** Displayed information. */ + int displayItem; + /** Scan display modality. */ + bool displayScanLines; + /** Information text modality. */ + bool verbose; + /** Information text style. */ + QPen infoPen; + /** Information text vertical offset. */ + int textOffset; + + + /** + * \brief Draws blurred segment structure information for the given step. + * @param painter : Painter to be decorated. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + void paintBlurredSegment (QPainter *painter, int step); + + /** + * \brief Draws connected components information for the given step. + * @param painter : Painter to be decorated. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + void paintConnectedComponents (QPainter *painter, int step); + + /** + * \brief Draws scans and filter information for the given step. + * @param painter : Painter to be decorated. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + void paintScansAndFilter (QPainter *painter, int step); + + /** + * \brief Draws a vector of pixels with given color. + * @param painter : Painter to be decorated. + * @param pix : Pixels to display. + * @param col : Pixels color. + */ + void paintPixels (QPainter *painter, + const vector<Pt2i> &pix, const QColor col); + + /** + * \brief Draws a list of image pixels. + * @param painter : Painter to be decorated. + * @param pix : List of pixels to display. + * @param col : Pixels color. + */ + void paintPixels (QPainter *painter, const vector<Pt2i> &pix); + + /** + * \brief Draws a pixel with given color. + * @param painter : Painter to be decorated. + * @param pix : Pixel to display. + * @param col : Color to apply. + */ + void paintPixel (QPainter *painter, const Pt2i &pix, const QColor col); + + /** + * \brief Initializes the text display (color and position). + * @param painter : Painter to be decorated. + */ + void initText (QPainter *painter); + + /** + * \brief Paints a new text in the graphics item (updates text position). + * @param painter : Painter to be decorated. + * @param text : Text to be displayed. + */ + void addText (QPainter *painter, const QString &text); +}; + +#endif diff --git a/Code/Seg/BSTools/bsstructureview.cpp b/Code/Seg/BSTools/bsstructureview.cpp new file mode 100755 index 0000000000000000000000000000000000000000..81ce0b946e5641f187850af4a5570b56796556d8 --- /dev/null +++ b/Code/Seg/BSTools/bsstructureview.cpp @@ -0,0 +1,234 @@ +#include <QtGui> +#include <iostream> +#include "bsstructureview.h" +#include "math.h" + +using namespace std; + + +BSStructureView::BSStructureView (QImage *im, BSDetector *sd) +{ + graylevelImage = im; + currentImage = im; + det = sd; + int w = im->width (); + int h = im->height (); + imageInBack = true; + gradImage = NULL; + gradInBack = false; + setBackgroundBrush (QBrush (*currentImage)); + setScene (new QGraphicsScene (0, 0, w, h)); + grid = new BSStructureItem (w, h, im, sd); + scene()->addItem (grid); + setWindowTitle (grid->itemTitle ()); +} + + +BSStructureView::~BSStructureView () +{ + scene()->removeItem (grid); + delete grid; + delete gradImage; +} + + +void BSStructureView::setGradientImage (VMap *gMap) +{ + if (gradImage != NULL) delete gradImage; + int w = gMap->getWidth (); + int h = gMap->getHeight (); + double gn[w * h]; + for (int j = 0; j < h; j++) + for (int i = 0; i < w; i++) + gn[j * w + i] = gMap->cmpNorm (i, j); + // gn[j * w + i] = sqrt (gMap->sqNorm (i, j)); + double max = 0; + for (int i = 0; i < w * h; i++) if (max < gn[i]) max = gn[i]; + gradImage = new QImage (w, h, QImage::Format_RGB32); + for (int j = 0; j < h; j++) + for (int i = 0; i < w; i++) + { + uint val = (uint) (gn[(h - 1 - j) * w + i] * 255 / max); + gradImage->setPixel (i, j, val + val * 256 + val * 256 * 256); + } + // gradImage->save ("gradient.png"); +} + + +void BSStructureView::updateBackground (bool zoomChanged) +{ + if (imageInBack) + { + int w = currentImage->width (); + int h = currentImage->height (); + int zf = grid->zoomFactor (); + if (zoomChanged) image = currentImage->scaled (w * zf, h * zf); + QBrush qb (image); + qb.setTransform (QTransform::fromTranslate ( + w / 2 - zf * (w / 2 + grid->focusX ()), + h / 2 - zf * (h / 2 + grid->focusY ()))); + setBackgroundBrush (qb); + } +} + + +void BSStructureView::toggleBackground () +{ + imageInBack = ! imageInBack; + if (imageInBack) setBackgroundBrush (QBrush (image)); + else setBackgroundBrush (QBrush (Qt::white)); +} + + +void BSStructureView::toggleGradient () +{ + if (currentImage == gradImage) currentImage = graylevelImage; + else if (gradImage != NULL) currentImage = gradImage; +} + + +void BSStructureView::paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED (painter); + Q_UNUSED (option); + Q_UNUSED (widget); +} + + +bool BSStructureView::processKeyEvent (QKeyEvent *event) +{ + bool processed = false; + switch (event->key ()) + { + case Qt::Key_I : // Info + if (event->modifiers () & Qt::ControlModifier) grid->switchScanDisplay (); + else grid->toggleDisplay ((event->modifiers () & Qt::ShiftModifier) == 0); + setWindowTitle (grid->itemTitle ()); + update (); + processed = true; + break; + + case Qt::Key_V : // Info display + grid->switchInfoDisplay (); + update (); + processed = true; + break; + + case Qt::Key_B : // Background + if (event->modifiers () & Qt::ShiftModifier) toggleGradient (); + else toggleBackground (); + updateBackground (true); + update (); + processed = true; + break; + + case Qt::Key_Plus : // Zoom in + grid->zoomIn (); + updateBackground (true); + update (); + processed = true; + break; + + case Qt::Key_Minus : // Zoom out + grid->zoomOut (); + updateBackground (true); + update (); + processed = true; + break; + + case Qt::Key_Left : // Go leftwards + grid->shift (-1, 0); + updateBackground (false); + update (); + processed = true; + break; + + case Qt::Key_Right : // Go rightwards + grid->shift (1, 0); + updateBackground (false); + update (); + processed = true; + break; + + case Qt::Key_Up : // Go upwards + grid->shift (0, -1); + updateBackground (false); + update (); + processed = true; + break; + + case Qt::Key_Down : // Go downwards + grid->shift (0, 1); + updateBackground (false); + update (); + processed = true; + break; + + case Qt::Key_C : // Increments the connected components min size + det->incConnectedComponentMinSize ( + (event->modifiers () & Qt::ShiftModifier) == 0); + update (); + processed = true; + break; + + case Qt::Key_G : // Increments the connected components min size + det->switchConnectivityConstraint (); + update (); + processed = true; + break; + + case Qt::Key_S : // Switch dynamic scans + det->switchDynamicScans (); + update (); + processed = true; + break; + + case Qt::Key_O : // Switch orthographic scans + det->switchOrthoScans (); + update (); + processed = true; + break; + + case Qt::Key_P : // Switch pre-filtering + det->switchFiltering (0); + update (); + processed = true; + break; + + case Qt::Key_F : // Switch final filtering + det->switchFiltering (1); + update (); + processed = true; + break; + + case Qt::Key_D : // Initial detection extension limitation + det->switchInitialBounding (); + update (); + processed = true; + break; + + case Qt::Key_W : // Input max width + det->setInputMaxWidth (det->getInputMaxWidth () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + update (); + processed = true; + break; + + case Qt::Key_L : // Output blurred segment min size + det->setBSminSize (det->getBSminSize () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + update (); + processed = true; + break; + + case Qt::Key_H : // Pixel lack tolerence + det->setPixelLackTolerence (det->getPixelLackTolerence () + + (event->modifiers () & Qt::ShiftModifier ? -1 : 1)); + update (); + processed = true; + break; + } + return processed; +} diff --git a/Code/Seg/BSTools/bsstructureview.h b/Code/Seg/BSTools/bsstructureview.h new file mode 100755 index 0000000000000000000000000000000000000000..6f6dce8cd82ee41ab354ae57e436922a5e9867c6 --- /dev/null +++ b/Code/Seg/BSTools/bsstructureview.h @@ -0,0 +1,80 @@ +#ifndef BS_STRUCTURE_VIEW_H +#define BS_STRUCTURE_VIEW_H + +#include <QGraphicsView> +#include <QImage> +#include "bsstructureitem.h" + + +class BSStructureView : public QGraphicsView +{ + +public: + + /** + * \brief Creates a pixel analyzer. + */ + BSStructureView (QImage *im, BSDetector *sd); + + /** + * \brief Deletes the pixel analyzer. + */ + ~BSStructureView (); + + /** + * Sets the gradient image. + */ + void setGradientImage (VMap *gMap); + + /** + * Toggles the window background between the current image or plain white. + */ + void toggleBackground (); + + /** + * Toggles the current image between gray level or gradient image. + */ + void toggleGradient (); + + /** + * \brief Redraws the pixel analyzer. + */ + void paint (QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget); + + /** + * \brief Processes key pressed events. + */ + bool processKeyEvent (QKeyEvent *event); + +protected: + +private: + + + /** Pointer to the blurred segment detector. */ + BSDetector *det; + /** Pointer to the raw image. */ + QImage *graylevelImage; + /** Pointer to the blurred segment structure graphics item. */ + BSStructureItem *grid; + /** Background image. */ + QImage image; + /** Background image display modality. */ + bool imageInBack; + /** Pointer to the gradient image. */ + QImage *currentImage; + /** Pointer to the currently displayed image. */ + QImage *gradImage; + /** Gradient image display modality. */ + bool gradInBack; + + /** + * Updates the background image. + * @param zoomChanged If 'true', the zoom factorshould be taken into account + */ + void updateBackground (bool zoomChanged); + +}; + +#endif diff --git a/Code/Seg/BSTools/bswindow.cpp b/Code/Seg/BSTools/bswindow.cpp new file mode 100755 index 0000000000000000000000000000000000000000..5c848f9339f57b3a09ab31f9bb5a41f22af4f0be --- /dev/null +++ b/Code/Seg/BSTools/bswindow.cpp @@ -0,0 +1,140 @@ +#include "bswindow.h" +#include <QtGui> +//#include <QtWidgets> +#include <QFileDialog> +#include <QMenuBar> +#include <iostream> + + +BSWindow::BSWindow (int *val) +{ + Q_UNUSED (val); + showProf = false; + showAccu = false; + showSeg = false; + gradType = VMap::TYPE_SOBEL_5X5; + detectionWidget = new BSDetectionWidget; + setCentralWidget (detectionWidget); + // setFocus(); + // createActions (); + // createMenus (); + setWindowTitle (tr ("Blurred Segments")); + resize (400, 400); +} + + +BSWindow::BSWindow () +{ + showProf = false; + showAccu = false; + showSeg = false; + gradType = 0; + detectionWidget = new BSDetectionWidget; + setCentralWidget (detectionWidget); + // setFocus (); + // createActions (); + // createMenus (); + setWindowTitle (tr ("Blurred Segments")); + resize (400, 400); +} + + +void BSWindow::setFile (QString fileName) +{ + resize (detectionWidget->openImage (fileName, gradType)); +} + + +void BSWindow::runOptions () +{ + if (showProf) detectionWidget->switchProfileAnalyzer (); + if (showAccu) detectionWidget->switchAccuAnalyzer (); + if (showSeg) detectionWidget->switchPixelAnalyzer (); +} + + +void BSWindow::runTest () +{ + detectionWidget->localTest (); +} + + +void BSWindow::closeEvent (QCloseEvent *event) +{ + detectionWidget->closeProfileAnalyzer (); + detectionWidget->closeAccuAnalyzer (); + detectionWidget->closePixelAnalyzer (); + event->accept (); +} + + +void BSWindow::open () +{ + QSize windowSize; + QString fileName = QFileDialog::getOpenFileName (this, + tr ("Open File"), QDir::currentPath ()); + if (! fileName.isEmpty ()) + { + windowSize = detectionWidget->openImage (fileName, gradType); + updateActions (); + } + resize (windowSize); +} + + +void BSWindow::save () +{ + QAction *action = qobject_cast < QAction *> (sender ()); + QByteArray fileFormat = action->data().toByteArray (); + saveFile (fileFormat); +} + + +void BSWindow::updateActions () +{ +} + + +void BSWindow::createActions () +{ + openAct = new QAction (tr ("&Open..."), this); + openAct->setShortcut (tr ("Ctrl+O")); + connect (openAct, SIGNAL (triggered ()), this, SLOT (open())); + + foreach (QByteArray format, QImageWriter::supportedImageFormats ()) + { + QString text = tr("%1...").arg (QString(format).toUpper ()); + QAction *action = new QAction (text, this); + action->setData (format); + connect (action, SIGNAL (triggered ()), this, SLOT (save ())); + saveAsActs.append (action); + } + exitAct = new QAction (tr ("E&xit"), this); + exitAct->setShortcut (tr ("Ctrl+Q")); + connect (exitAct, SIGNAL (triggered ()), this, SLOT (close ())); +} + + +void BSWindow::createMenus () +{ + saveAsMenu = new QMenu (tr ("&Save As"), this); + foreach (QAction *action, saveAsActs) saveAsMenu->addAction (action); + fileMenu = new QMenu (tr ("&File"), this); + fileMenu->addAction (openAct); + fileMenu->addMenu (saveAsMenu); + fileMenu->addSeparator (); + fileMenu->addAction (exitAct); + menuBar()->addMenu (fileMenu); +} + + +bool BSWindow::saveFile (const QByteArray &fileFormat) +{ + QString initialPath = QDir::currentPath () + "/untitled." + fileFormat; + QString fileName = QFileDialog::getSaveFileName (this, tr ("Save As"), + initialPath, tr ("%1 Files (*.%2);;All Files (*)") + .arg(QString(fileFormat.toUpper())) + .arg(QString(fileFormat))); + if (fileName.isEmpty ()) return false; + else return detectionWidget->saveAugmentedImage (fileName, fileFormat); +} diff --git a/Code/Seg/BSTools/bswindow.h b/Code/Seg/BSTools/bswindow.h new file mode 100755 index 0000000000000000000000000000000000000000..c9afa16d0ed1ef3176dfb455805d8fd684f5bd67 --- /dev/null +++ b/Code/Seg/BSTools/bswindow.h @@ -0,0 +1,96 @@ +#ifndef BS_WINDOW_H +#define BS_WINDOW_H + +#include <QList> +#include <QMainWindow> +#include "bsdetectionwidget.h" + + +/** + * @class BSWindow bswindow.h + * \brief Blurred segment extraction Qt window. + * \author {P. Even and B. Kerautret} + */ +class BSWindow : public QMainWindow +{ + Q_OBJECT + +public: + + /** + * \brief Creates a blurred segment extraction window. + */ + BSWindow (); + + /** + * \brief Creates a segment extraction window. + */ + BSWindow (int *val); + + /** + * Sets the processed image. + */ + void setFile (QString fileName); + + /** + * Switches the profile analysis window on or off. + */ + inline void toggleProfWindow () { showProf = ! showProf; } + + /** + * Switches the accumulator analysis window on or off. + */ + inline void toggleAccuWindow () { showAccu = ! showAccu; } + + /** + * Switches the segment analysis window on or off. + */ + inline void toggleSegWindow () { showSeg= ! showSeg; } + + /** + * Sets the gradient extraction method to be used. + */ + inline void useGradient (int type) { gradType = type; } + + /** + * Takes into account the option (after image load). + */ + void runOptions (); + + /** + * Performs a simple test. + */ + void runTest (); + + +protected: + void closeEvent (QCloseEvent *event); + + +private slots: + void open (); + void save (); + void updateActions (); + + +private: + + /** Blurred segment detection widget. */ + BSDetectionWidget *detectionWidget; + bool showProf; + bool showAccu; + bool showSeg; + int gradType; + + void createActions (); + void createMenus (); + bool saveFile (const QByteArray &fileFormat); + QMenu *saveAsMenu; + QMenu *fileMenu; + + QAction *openAct; + QList<QAction *> saveAsActs; + QAction *exitAct; + +}; +#endif diff --git a/Code/Seg/BlurredSegment/biptlist.cpp b/Code/Seg/BlurredSegment/biptlist.cpp new file mode 100755 index 0000000000000000000000000000000000000000..233932bd8f3196653272295ed44104247de0f96c --- /dev/null +++ b/Code/Seg/BlurredSegment/biptlist.cpp @@ -0,0 +1,103 @@ +#include "biptlist.h" +#include <iostream> + + +BiPtList::BiPtList (Pt2i pt) +{ + pts.push_back (pt); + start = 0; + cpt = 1; +} + + +BiPtList::~BiPtList () +{ +} + + +void BiPtList::addFront (Pt2i pt) +{ + pts.push_front (pt); + start++; + cpt++; +} + + +void BiPtList::addBack (Pt2i pt) +{ + pts.push_back (pt); + cpt++; +} + + +void BiPtList::removeFront (int n) +{ + if (n >= frontSize ()) n = frontSize () - 1; // We keep at least one point + for (int i = 0; i < n; i++) pts.pop_front (); + cpt -= n; + start -= n; + if (start < 0) start = 0; // Theoretically impossible +} + + +void BiPtList::removeBack (int n) +{ + if (n >= backSize ()) n = backSize () - 1; // We keep at least one point + for (int i = 0; i < n; i++) pts.pop_back (); + cpt -= n; + if (start >= cpt) start = cpt - 1; // Theoretically impossible +} + + +void BiPtList::findExtrema (int &xmin, int &ymin, int &xmax, int &ymax) const +{ + deque<Pt2i>::const_iterator it = pts.begin (); + xmin = it->x (); + ymin = it->y (); + xmax = it->x (); + ymax = it->y (); + while (it != pts.end ()) + { + if (xmin > it->x ()) xmin = it->x (); + if (xmax < it->x ()) xmax = it->x (); + if (ymin > it->y ()) ymin = it->y (); + if (ymax < it->y ()) ymax = it->y (); + it++; + } +} + + +vector<Pt2i> BiPtList::frontToBackPoints () const +{ + vector<Pt2i> res; + for (deque<Pt2i>::const_iterator it = pts.begin (); + it != pts.end (); it++) + res.push_back (*it); + return (res); +} + + +vector<Pt2i> *BiPtList::vide () const +{ + return (new vector<Pt2i> ()); +} + + +vector<Pt2i> *BiPtList::frontPoints () const +{ + // Fournis du bord au centre : pertinent ? + vector<Pt2i> *res = new vector<Pt2i> (); + deque<Pt2i>::const_iterator it = pts.begin (); + for (int i = 0; i < start; i++) res->push_back (*it++); + return res; +} + + +vector<Pt2i> *BiPtList::backPoints () const +{ + vector<Pt2i> *res = new vector<Pt2i> (); + deque<Pt2i>::const_iterator it = pts.begin (); + it += start + 1; + for (int i = 0; i < cpt - start - 1; i++) res->push_back (*it++); + return res; +} diff --git a/Code/Seg/BlurredSegment/biptlist.h b/Code/Seg/BlurredSegment/biptlist.h new file mode 100755 index 0000000000000000000000000000000000000000..be01993e2553f7a34c0bf98f700e34b289666beb --- /dev/null +++ b/Code/Seg/BlurredSegment/biptlist.h @@ -0,0 +1,132 @@ +#ifndef BIPT_LIST_H +#define BIPT_LIST_H + +#include "pt2i.h" +#include <deque> + +using namespace std; + + +/** + * @class BiPtList biptlist.h + * \brief Bi-directional list of points. + * \author {P. Even} + */ +class BiPtList +{ +public: + + /** + * Creates a extendable bi-directional list with one point. + * @param pt Initial point of the list. + */ + BiPtList (Pt2i pt); + + /** + * Deletes the bi-directional list. + */ + ~BiPtList (); + + /** + * Returns the count of point in the bi-directional list. + */ + inline int size () const { return (cpt); } + + /** + * Returns the count of point on the back part of the bi-directional list. + */ + inline int backSize () const { return (cpt - start - 1); } + + /** + * Returns the count of point on the back part of the bi-directional list. + */ + inline int frontSize () const { return (start); } + + /** + * Returns the initial point of the bi-directional list. + */ + inline Pt2i initialPoint () const { return (pts[start]); } + + /** + * Returns the back end point of the bi-directional list. + */ + inline Pt2i backPoint () const { return (pts.back ()); } + + /** + * Returns the front end point of the bi-directional list. + */ + inline Pt2i frontPoint () const { return (pts.front ()); } + + /** + * Returns the height of the point to the line between the list end points.. + */ + inline AbsRat heightToEnds (const Pt2i &pt) const { + return (pt.triangleRationalHeight (pts.front (), pts.back ())); } + + /** + * Adds the point on front side. + * @param pt The point to add. + */ + void addFront (Pt2i pt); + + /** + * Adds the point on back side. + * @param pt The point to add. + */ + void addBack (Pt2i pt); + + /** + * Removes n point on front side. + * @param n The count of points to remove. + */ + void removeFront (int n); + + /** + * Removes n point on back side. + * @param n The count of points to remove. + */ + void removeBack (int n); + + /** + * Fills in the given parameters with the point min and max coordinates. + * @param xmin Minimum X value. + * @param ymin Minimum Y value. + * @param xmax Maximum X value. + * @param ymax Maximum Y value. + */ + void findExtrema (int &xmin, int &ymin, int &xmax, int &ymax) const; + + /** + * Returns front to back points in a vector. + */ + vector<Pt2i> frontToBackPoints () const; + + /** + * Returns a pointer to a empty vector. + */ + vector<Pt2i> *vide () const; + + /** + * Returns a pointer to a vector filled in with front points. + * Front points are entered from segment edge to the initial point excluded. + */ + vector<Pt2i> *frontPoints () const; + + /** + * Returns a pointer to a vector filled in with back points. + * Back points are entered from initial point excluded to segment edge. + */ + vector<Pt2i> *backPoints () const; + + +private: + + /** List of points. */ + deque<Pt2i> pts; + /** Index of the initial point. */ + int start; + /** Length of the point list. */ + int cpt; + +}; +#endif diff --git a/Code/Seg/BlurredSegment/blurredsegment.cpp b/Code/Seg/BlurredSegment/blurredsegment.cpp new file mode 100755 index 0000000000000000000000000000000000000000..7dcc136d266d9a8bf825f83f55b91e3b9744c284 --- /dev/null +++ b/Code/Seg/BlurredSegment/blurredsegment.cpp @@ -0,0 +1,260 @@ +#include "blurredsegment.h" + + +BlurredSegment::BlurredSegment () +{ + plist = NULL; + dss = NULL; +} + + +BlurredSegment::BlurredSegment (BiPtList *ptlist, DigitalStraightSegment *seg) +{ + plist = ptlist; + dss = seg; +} + + +BlurredSegment::~BlurredSegment () +{ + if (plist != NULL) delete plist; + if (dss != NULL) delete dss; +} + + +AbsRat BlurredSegment::segmentRationalWidth () const +{ + return (AbsRat (dss->width (), dss->period ())); +} + + +vector<Pt2i> BlurredSegment::getAllPoints () const +{ + return (plist->frontToBackPoints ()); +} + + +vector<Pt2i> *BlurredSegment::getAllRight () const +{ + return (plist->backPoints ()); +} + + +vector<Pt2i> *BlurredSegment::getAllLeft () const +{ + return (plist->frontPoints ()); +} + + +int BlurredSegment::size () const +{ + return (plist->size ()); +} + + +vector<Pt2i> BlurredSegment::getStartPt () const +{ + vector<Pt2i> res; + res.push_back (plist->initialPoint ()); + return res; +} + + +const Pt2i BlurredSegment::getLastRight () const +{ + return (plist->backPoint ()); +} + + +const Pt2i BlurredSegment::getLastLeft () const +{ + return (plist->frontPoint ()); +} + + +Vr2i BlurredSegment::getSupportVector () +{ + return (dss->supportVector ()); +} + + +Vr2i BlurredSegment::boundingBoxSize () const +{ + int xmin, ymin, xmax, ymax; + plist->findExtrema (xmin, ymin, xmax, ymax); + return (Vr2i (xmax - xmin, ymax - ymin)); +} + + +vector <vector <Pt2i> > BlurredSegment::connectedComponents () const +{ + vector <vector <Pt2i> > ccs; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + vector <Pt2i> cc; + bool started = false; + vector <Pt2i>::const_iterator it = pts.begin (); + Pt2i pix = *it++; + while (it != pts.end ()) + { + if (it->isConnectedTo (pix)) + { + if (! started) + { + cc.push_back (pix); + started = true; + } + cc.push_back (*it); + } + else + { + if (started) + { + ccs.push_back (cc); + cc.clear (); + started = false;; + } + } + pix = *it++; + } + } + return ccs; +} + + +int BlurredSegment::countOfConnectedPoints () const +{ + int count = 0; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + bool started = false; + vector <Pt2i>::const_iterator it = pts.begin (); + Pt2i pix = *it++; + while (it != pts.end ()) + { + if (it->isConnectedTo (pix)) + { + if (started) count ++; + else + { + count += 2; + started = true; + } + } + else + { + if (started) started = false;; + } + pix = *it++; + } + } + return count; +} + + +int BlurredSegment::countOfConnectedComponents () const +{ + int count = 0; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + bool started = false; + vector <Pt2i>::const_iterator it = pts.begin (); + Pt2i pix = *it++; + while (it != pts.end ()) + { + if (it->isConnectedTo (pix)) + { + if (! started) + { + count ++; + started = true; + } + } + else + { + if (started) started = false;; + } + pix = *it++; + } + } + return count; +} + + +int BlurredSegment::countOfConnectedPoints (int min) const +{ + int count = 0; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + int cpt = 1; + vector <Pt2i>::const_iterator it = pts.begin (); + Pt2i pix = *it++; + while (it != pts.end ()) + { + if (it->isConnectedTo (pix)) + { + if (++cpt == min) count += min; + else if (cpt > min) count ++; + } + else cpt = 1; + pix = *it++; + } + } + return count; +} + + +int BlurredSegment::countOfConnectedComponents (int min) const +{ + int count = 0; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + int cpt = 1; + vector <Pt2i>::const_iterator it = pts.begin (); + Pt2i pix = *it++; + while (it != pts.end ()) + { + if (it->isConnectedTo (pix)) + { + if (++cpt == min) count ++; + } + else cpt = 1;; + pix = *it++; + } + } + return count; +} + + +vector <vector <Pt2i> > +BlurredSegment::getConnectedComponents () const +{ + vector < vector <Pt2i> > res; + vector <Pt2i> pts = getAllPoints (); + if (pts.size () > 1) + { + vector <Pt2i>::const_iterator bit = pts.begin (); + vector <Pt2i>::const_iterator eit = pts.end (); + while (bit != eit) + { + vector <Pt2i> lres; + Pt2i pix = *bit++; + bool compose = true; + do + { + lres.push_back (pix); + compose = bit->isConnectedTo (pix); + if (compose) pix = *bit++; + } + while (compose && bit != eit); + if (compose) lres.push_back (pix); + res.push_back (lres); + } + } + return res; +} diff --git a/Code/Seg/BlurredSegment/blurredsegment.h b/Code/Seg/BlurredSegment/blurredsegment.h new file mode 100755 index 0000000000000000000000000000000000000000..0395e78a75033268afc98cdbd4024448e2714c7f --- /dev/null +++ b/Code/Seg/BlurredSegment/blurredsegment.h @@ -0,0 +1,171 @@ +#ifndef BLURRED_SEGMENT_H +#define BLURRED_SEGMENT_H + + +#include "convexhull.h" +#include "digitalstraightsegment.h" +#include "biptlist.h" + +using namespace std; + + +/** + * @class BlurredSegment blurredsegment.h + * \brief A list of 2D points lying inside a digital straight line. + * \author {P. Even} + */ +class BlurredSegment +{ + +public: + + /** + * Creates a blurred segment from nothing. + */ + BlurredSegment (); + + /** + * Creates a blurred segment from a list of points. + * @param ptlist List of points of the blurred segment to build. + * @param seg Bounding digital straight segment. + */ + BlurredSegment (BiPtList *ptlist, DigitalStraightSegment *seg); + + /** + * \brief Deletes the blurred segment. + */ + virtual ~BlurredSegment (); + + /** + * \brief Returns the minimal vertical or horizontal width. + */ + virtual AbsRat segmentRationalWidth () const; + + /** + * \brief Returns if the segment has non null thickness (not aligned points). + */ + inline bool isThick () const { return (dss->width () < 2); } + + /** + * \brief Returns the underlying digital straight segment. + */ + inline DigitalStraightSegment *getSegment () { return dss; } + + /** + * \brief Returns the count of points of the blurred segment. + */ + int size () const; + + /** + * \brief Returns the start point of the blurred segment. + */ + inline const Pt2i getCenter () const { return plist->initialPoint (); } + + /** + * \brief Returns the colinear points at the left of the start point. + */ + inline const vector<Pt2i> *getLeftLine () const { return plist->vide (); } + + /** + * \brief Returns the colinear points at the right of the start point. + */ + inline const vector<Pt2i> *getRightLine () const { return plist->vide (); } + + /** + * \brief Returns the left points added to the blurred segment start point. + */ + inline const vector<Pt2i> *getLeftPoints () const { + return plist->frontPoints (); } + + /** + * \brief Returns the right points added to the blurred segment start point. + */ + inline const vector<Pt2i> *getRightPoints () const { + return plist->backPoints (); } + + /** + * \brief Returns the set of all the points on the blurred segment. + * Points are ordered from the left end point up to the right end point. + */ + vector<Pt2i> getAllPoints () const; + + /** + * \brief Returns the set of points on the left part of the blurred segment. + * Points are ordered from the furthest to the nearest to the start point. + */ + vector<Pt2i> *getAllLeft () const; + + /** + * \brief Returns the set of points on the left part of the blurred segment. + * Points are ordered from the nearest to the furthest to the start point. + */ + vector<Pt2i> *getAllRight () const; + + /** + * \brief Returns a vector containing the start point of the blurred segment. + */ + vector<Pt2i> getStartPt () const; + + /** + * \brief Returns the last accepted point on the right side. + */ + const Pt2i getLastRight () const; + + /** + * \brief Returns the last accepted point on the left side. + */ + const Pt2i getLastLeft () const; + + /** + * \brief Returns the support vector of the blurred segment. + */ + virtual Vr2i getSupportVector (); + + /** + * \brief Returns the size of the segment bounding box in a vector 2D. + */ + Vr2i boundingBoxSize () const; + + /** + * \brief Returns the connected components of the blurred segment. + */ + vector <vector <Pt2i> > connectedComponents () const; + + /** + * \brief Returns the count of connected points in the blurred segment. + */ + int countOfConnectedPoints () const; + + /** + * \brief Returns the count of connected components in the blurred segment. + */ + int countOfConnectedComponents () const; + + /** + * \brief Returns the count of connected points of given minimal size. + * @param min Minimal size of the connected components. + */ + int countOfConnectedPoints (int min) const; + + /** + * \brief Returns the count of connected components of given minimal size. + * @param min Minimal size of the connected components. + */ + int countOfConnectedComponents (int min) const; + + /** + * \brief Returns the connected components of the blurred segment. + */ + vector <vector <Pt2i> > getConnectedComponents () const; + + +protected: + + /** Bounding straight segment. */ + DigitalStraightSegment *dss; + + /** Bi-directional list of points. */ + BiPtList *plist; + +}; +#endif diff --git a/Code/Seg/BlurredSegment/blurredsegmentproto.cpp b/Code/Seg/BlurredSegment/blurredsegmentproto.cpp new file mode 100755 index 0000000000000000000000000000000000000000..2354df5d1ed901d2386dbb1a0c0254d12e532968 --- /dev/null +++ b/Code/Seg/BlurredSegment/blurredsegmentproto.cpp @@ -0,0 +1,284 @@ +#include "blurredsegmentproto.h" + + +BlurredSegmentProto::BlurredSegmentProto (int maxWidth, Pt2i pix) +{ + this->maxWidth.set (maxWidth); + plist = new BiPtList (pix); + leftOK = false; + rightOK = false; + bsFlat = false; + bsOK = false; + convexhull = NULL; + chChanged = false; + dss = NULL; +} + + +BlurredSegmentProto::BlurredSegmentProto (int maxWidth, Pt2i center, + const vector<Pt2i> &leftPts, const vector<Pt2i> &rightPts) +{ + this->maxWidth.set (maxWidth); + plist = new BiPtList (center); + leftOK = false; + rightOK = false; + bsFlat = false; + bsOK = false; + convexhull = NULL; + chChanged = false; + dss = NULL; + + vector<Pt2i>::const_iterator itr = rightPts.begin (); + vector<Pt2i>::const_iterator itl = leftPts.begin (); + bool scanningRight = true; + bool scanningLeft = true; + while (scanningRight || scanningLeft) + { + if (scanningRight) + { + if (itr == rightPts.end ()) + { + scanningRight = false; + rightOK = true; + } + else plist->addBack (*itr++); + } + if (scanningLeft) + { + if (itl == leftPts.end ()) scanningLeft = false; + else + { + plist->addFront (*itl++); + leftOK = true; + } + } + if (! bsOK && plist->size () > 2) + { + if (! plist->backPoint().colinearTo (plist->frontPoint (), + plist->initialPoint ())) + bsOK = true; + } + } +} + + +BlurredSegmentProto::~BlurredSegmentProto () +{ + if (convexhull != NULL) delete convexhull; +} + + +AbsRat BlurredSegmentProto::segmentRationalWidth () const +{ + return (convexhull != NULL ? convexhull->rationalThickness () + : AbsRat (0, 1)); +} + + +DigitalStraightLine *BlurredSegmentProto::getLine () const +{ + if (bsOK) + { + Pt2i s, e, v; + convexhull->antipodalEdgeAndVertex (s, e, v); + return (new DigitalStraightLine (s, e, v)); + } + if (bsFlat) return (new DigitalStraightLine (getLastLeft (), getLastRight (), + DigitalStraightLine::DSL_THIN)); + return (NULL); +} + + +bool BlurredSegmentProto::addLeft (Pt2i pix) +{ + if (bsOK) // convexhull defined + { + bool res = addPoint (pix, true); + return (res); + } + + else if (bsFlat) // leftOK && rightOK + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () != 0) + { + convexhull = new ConvexHull (pix, plist->frontPoint (), + plist->backPoint ()); + bsOK = true; + } + plist->addFront (pix); + } + + else if (leftOK) // thus ! rightOK + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () == 0) bsFlat = true; + else + { + convexhull = new ConvexHull (pix, plist->frontPoint (), + plist->backPoint ()); + bsOK = true; + } + plist->addFront (pix); + } + + else if (rightOK) + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () == 0) bsFlat = true; + else + { + convexhull = new ConvexHull (pix, plist->frontPoint (), + plist->backPoint ()); + bsOK = true; + } + plist->addFront (pix); + } + + else // only the central point is ok + { + plist->addFront (pix); + leftOK = true; + } + + chChanged = true; + return true; +} + + +bool BlurredSegmentProto::addRight (Pt2i pix) +{ + if (bsOK) // bs != NULL + { + bool res = addPoint (pix, false); + return (res); + } + + else if (bsFlat) // leftOK && rightOK + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () != 0) + { + convexhull = new ConvexHull (plist->frontPoint (), + plist->backPoint (), pix); + bsOK = true; + } + plist->addBack (pix); + } + + else if (rightOK) // thus ! leftOK + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () == 0) bsFlat = true; + else + { + convexhull = new ConvexHull (plist->frontPoint (), + plist->backPoint (), pix); + bsOK = true; + } + plist->addBack (pix); + } + + else if (leftOK) + { + AbsRat height = plist->heightToEnds (pix); + if (height.greaterThan (maxWidth)) return false; + if (height.numerator () == 0) bsFlat = true; + else + { + convexhull = new ConvexHull (plist->frontPoint (), + plist->backPoint (), pix); + bsOK = true; + } + plist->addBack (pix); + } + + else // only the central point is ok + { + plist->addBack (pix); + rightOK = true; + } + + chChanged = true; + return true; +} + + +bool BlurredSegmentProto::addPoint (Pt2i p, bool onleft) +{ + bool inserted = convexhull->addPointDS (p, onleft); + if ((segmentRationalWidth ()).greaterThan (maxWidth)) + { + if (inserted) convexhull->restore (); + return false; + } + + if (onleft) plist->addFront (p); + else plist->addBack (p); + chChanged = true; + return true; +} + + +void BlurredSegmentProto::removeLeft (int n) +{ + if (bsOK) plist->removeFront (n); +} + + +void BlurredSegmentProto::removeRight (int n) +{ + if (bsOK) plist->removeBack (n); +} + + +Vr2i BlurredSegmentProto::getSupportVector () +{ + if (bsOK) + { + Pt2i s, e, v; + convexhull->antipodalEdgeAndVertex (s, e, v); + return (s.vectorTo (e)); + } + if (bsFlat || leftOK || rightOK) + return (getLastLeft().vectorTo (getLastRight ())); + return (Vr2i (1, 0)); // hardly better with only one point ! +} + + +BlurredSegment *BlurredSegmentProto::endOfBirth () +{ + DigitalStraightSegment *seg = NULL; + if (bsOK) + { + int xmin, ymin, xmax, ymax; + plist->findExtrema (xmin, ymin, xmax, ymax); + Pt2i s, e, v; + convexhull->antipodalEdgeAndVertex (s, e, v); + seg = new DigitalStraightSegment (s, e, v, xmin, ymin, xmax, ymax); + } + else if (bsFlat || rightOK || leftOK) + { + Pt2i llast = plist->frontPoint (); + Pt2i rlast = plist->backPoint (); + int xmin = llast.x (); + if (rlast.x () < llast.x ()) xmin = rlast.x (); + int ymin = llast.y (); + if (rlast.y () < llast.y ()) ymin = rlast.y (); + int xmax = llast.x (); + if (rlast.x () > llast.x ()) xmax = rlast.x (); + int ymax = llast.y (); + if (rlast.y () > llast.y ()) ymax = rlast.y (); + seg = new DigitalStraightSegment (llast, rlast, + DigitalStraightLine::DSL_THIN, + xmin, ymin, xmax, ymax); + } + BlurredSegment *bbs = new BlurredSegment (plist, seg); + plist = NULL; // NECESSARY TO AVOID CONTENTS CLEARANCE !!! + return (bbs); +} diff --git a/Code/Seg/BlurredSegment/blurredsegmentproto.h b/Code/Seg/BlurredSegment/blurredsegmentproto.h new file mode 100755 index 0000000000000000000000000000000000000000..ba2b51da4632340040c54928a6dbe2b69e970237 --- /dev/null +++ b/Code/Seg/BlurredSegment/blurredsegmentproto.h @@ -0,0 +1,131 @@ +#ifndef BLURRED_SEGMENT_PROTO_H +#define BLURRED_SEGMENT_PROTO_H + + +#include "blurredsegment.h" + +using namespace std; + + +/** + * @class BlurredSegmentProto blurredsegmentproto.h + * \brief A prototype of blurred segment, untill complete specification. + * It is mostly based on a evolving list of points, its convex hull and + * the successive states of the blurred segment construction. + * \author {P. Even} + */ +class BlurredSegmentProto : public BlurredSegment +{ + +public: + + /** + * Creates a blurred segment prototype. + * @param maxWidth Maximal width of the blurred segment to build + * @param pix Central point of the blurred segment to build + */ + BlurredSegmentProto (int maxWidth, Pt2i pix); + + /** + * Creates a blurred segment prototype with lists of points. + * @param maxWidth Maximal width of the blurred segment to build. + * @param center Central point of the blurred segment to build. + * @param leftPts Points to add on left side + * @param rightPts Points to add on right side. + */ + BlurredSegmentProto (int maxWidth, Pt2i center, + const vector<Pt2i> &leftPts, const vector<Pt2i> &rightPts); + + /** + * \brief Deletes the blurred segment prototype. + */ + ~BlurredSegmentProto (); + + /** + * \brief Returns the minimal vertical or horizontal width. + */ + AbsRat segmentRationalWidth () const; + + /** + * \brief Returns the requested max width of the segment. + */ + inline const AbsRat getMaxWidth () const { return maxWidth; } + + /** + * \brief Sets the requested max width of the segment. + * @param maxwidth The new vaue for the requested max width. + */ + inline void setMaxWidth (const AbsRat &val) { maxWidth.set (val); } + + /** + * \brief Returns the underlying digital straight line. + */ + DigitalStraightLine *getLine () const; + + /** + * Adds a new point on the left. + * @param pix point to be added. + * Returns true if the point is accepted. + */ + bool addLeft (Pt2i pix); + + /** + * Adds a new point on the right. + * @param pix point to be added. + * Returns true if the point is accepted. + */ + bool addRight (Pt2i pix); + + /** + * \brief Remove last points on the left side. + * @param n Number of points to remove. + */ + void removeLeft (int n); + + /** + * \brief Remove last points on the right side. + * @param n Number of points to remove. + */ + void removeRight (int n); + + /** + * \brief Returns the support vector of the blurred segment. + */ + Vr2i getSupportVector (); + + /** + * \brief Returns a stable blurred segment from available information. + */ + BlurredSegment *endOfBirth (); + + +protected: + + /** Maximal width of the blurred segment. */ + AbsRat maxWidth; + + /** Maintained convex hull of the blurred segment. */ + ConvexHull *convexhull; + + /** Indicates if the blurred segment is constructed. */ + bool bsOK; + /** Indicates if the points are aligned. */ + bool bsFlat; + /** Indicates if the left point is defined. */ + bool leftOK; + /** Indicates if the right point is defined. */ + bool rightOK; + + /** Flag indicating if the convex hull changed since last DSS extraction. */ + bool chChanged; + + + /** + * \brief Submits a new point to extend the blurred segment. + * @param p Submitted point. + * @param onleft Adding direction (true for LEFT, false for RIGHT). + */ + bool addPoint (Pt2i p, bool onleft); + +}; +#endif diff --git a/Code/Seg/BlurredSegment/bsdetector.cpp b/Code/Seg/BlurredSegment/bsdetector.cpp new file mode 100755 index 0000000000000000000000000000000000000000..b52db0ef9ec62490c2d2e57f075117502c5519ec --- /dev/null +++ b/Code/Seg/BlurredSegment/bsdetector.cpp @@ -0,0 +1,289 @@ +#include "bsdetector.h" + + + +const int BSDetector::RESULT_UNDETERMINED = -1; +const int BSDetector::RESULT_OK = 0; +const int BSDetector::RESULT_INITIAL_NO_DETECTION = 11; +const int BSDetector::RESULT_INITIAL_TOO_FEW = 12; +const int BSDetector::RESULT_INITIAL_TOO_SPARSE = 13; +const int BSDetector::RESULT_INITIAL_TOO_MANY_OUTLIERS = 14; +const int BSDetector::RESULT_FINAL_NO_DETECTION = 21; +const int BSDetector::RESULT_FINAL_TOO_FEW = 22; +const int BSDetector::RESULT_FINAL_TOO_SPARSE = 23; +const int BSDetector::RESULT_FINAL_TOO_MANY_OUTLIERS = 24; + +const int BSDetector::DEFAULT_BS_MIN_SIZE = 5; +const int BSDetector::ABSOLUTE_BS_MIN_SIZE = 3; +const int BSDetector::DEFAULT_CONNECT_MIN_SIZE = 5; +const int BSDetector::DEFAULT_AUTO_RESOLUTION = 10; + + +BSDetector::BSDetector () +{ + gMap = NULL; + + bst1 = new BSTracker (); + bst1->setPixelLackTolerence (bst1->proximityThreshold ()); + bst2 = new BSTracker (); + + prefilteringOn = true; + lsf1 = (prefilteringOn ? new LineSpaceFilter () : NULL); + filteringOn = false; + lsf2 = (filteringOn ? new LineSpaceFilter () : NULL); + + edgeDirection = 0; // detects line (not only edges) + bsMinSize = DEFAULT_BS_MIN_SIZE; + connectMinSize = DEFAULT_CONNECT_MIN_SIZE; + minScanLength = bst1->defaultMinScan (); + autowidth = true; + ccOn = true; + densityTestOn = true; + multiSelection = false; + autoResol = DEFAULT_AUTO_RESOLUTION; + + bsini = NULL; + bsf = NULL; + tempo = true; + resultValue = RESULT_UNDETERMINED; +} + + +BSDetector::~BSDetector () +{ + delete bst1; + delete bst2; + if (lsf1 != NULL) delete lsf1; + if (lsf2 != NULL) delete lsf2; +} + + +void BSDetector::setGradientMap (VMap *data) +{ + gMap = data; + bst1->setGradientMap (data); + bst2->setGradientMap (data); +} + + +void BSDetector::detectAll () +{ + // Deletes former blurred segments + vector<BlurredSegment *>::iterator it = mbsf.begin (); + while (it != mbsf.end ()) delete (*it++); + mbsf.clear (); + + gMap->setMasking (true); + + int width = gMap->getWidth (); + int height = gMap->getHeight (); + for (int x = width / 2; x > 0; x -= autoResol) + runMultiDetection (Pt2i (x, 0), Pt2i (x, height - 1), Vr2i (0, 1)); + for (int x = width / 2 + autoResol; x < width - 1; x += autoResol) + runMultiDetection (Pt2i (x, 0), Pt2i (x, height - 1), Vr2i (0, 1)); + for (int y = height / 2; y > 0; y -= autoResol) + runMultiDetection (Pt2i (0, y), Pt2i (width - 1, y), Vr2i (1, 0)); + for (int y = height / 2 + autoResol; y < height - 1; y += autoResol) + runMultiDetection (Pt2i (0, y), Pt2i (width - 1, y), Vr2i (1, 0)); + + gMap->clearMask (); + gMap->setMasking (false); +} + + +void BSDetector::multidetect (const Pt2i &p1, const Pt2i &p2) +{ + // Deletes former blurred segments + vector<BlurredSegment *>::iterator it = mbsf.begin (); + while (it != mbsf.end ()) delete (*it++); + mbsf.clear (); + + gMap->setMasking (true); + runMultiDetection (p1, p2, p1.vectorTo (p2)); + gMap->clearMask (); + gMap->setMasking (false); +} + + +void BSDetector::runMultiDetection (const Pt2i &p1, const Pt2i &p2, + const Vr2i &dir) +{ + vector<Pt2i> pts; + p1.draw (pts, p2); + int locmax[pts.size ()]; + gMap->releaseOrientationConstraint (); + int nlm = gMap->localMax (locmax, pts, dir); + gMap->restoreOrientationConstraint (); + for (int i = 0; i < nlm; i++) + { + detect (p1, p2, &(pts.at (locmax[i]))); + if (bsf != NULL) + { + gMap->setMask (bsf->getAllPoints ()); + mbsf.push_back (bsf); + tempo = false; // to avoid former BS deletion + } + } +} + + +void BSDetector::detect (const Pt2i &p1, const Pt2i &p2, Pt2i *p0) +{ + // Clearance + //---------- + resultValue = RESULT_UNDETERMINED; + bst1->clear (); + bst2->clear (); + if (prefilteringOn) lsf1->clear (); + if (filteringOn) lsf2->clear (); + if (bsini != NULL) delete bsini; + if (tempo && bsf != NULL) delete bsf; + bsf = NULL; + tempo = true; + if (p1.equals (p2)) return; + + // Initial detection based on highest gradient without orientation constraint + //--------------------------------------------------------------------------- + bsini = bst1->fastTrack (p1, p2, p0); + if (bsini == NULL || bsini->size () < bsMinSize) + { + resultValue = (bsini == NULL ? RESULT_INITIAL_NO_DETECTION + : RESULT_INITIAL_TOO_FEW); + return; + } + + // Density test + //------------- + if (densityTestOn) + { + DigitalStraightLine mydsl (p1, p2, DigitalStraightLine::DSL_NAIVE); + int mydrlf = mydsl.manhattan (bsini->getLastRight ()) + - mydsl.manhattan (bsini->getLastLeft ()); + if (mydrlf < 0) mydrlf = -mydrlf; // Case of horizontal P1P2 + int expansion = 1 + mydrlf; + if (bsini->size () < expansion / 2) + { + resultValue = RESULT_INITIAL_TOO_SPARSE; + return; + } + } + + // Hough filtering on the initial segment + //--------------------------------------- + if (prefilteringOn) + { + BlurredSegment *fbs = lsf1->filter (bsini); + if (fbs != NULL) + { + delete bsini; + bsini = fbs; + } + } + if (bsini->size () < bsMinSize) + { + resultValue = RESULT_INITIAL_TOO_MANY_OUTLIERS; + return; + } + + // Finer detection based on gradient maxima with orientation constraint + //--------------------------------------------------------------------- + Pt2i pCenter = bsini->getCenter (); + Vr2i gRef = gMap->getValue (pCenter.x (), pCenter.y ()); + if (edgeDirection == -1) gRef.invert (); + int bswidth = bst1->inputMaxWidth (); + int scanwidth2 = 4 * bswidth; + if (autowidth) + { + bswidth = minScanLength; + DigitalStraightSegment *dss = bsini->getSegment (); + if (dss != NULL) + { + bswidth = (dss->width () / dss->period ()); + if (bswidth < minScanLength) bswidth = minScanLength; + } + scanwidth2 = bswidth; + pCenter = bsini->getSegment()->centerOfIntersection (p1, p2); + } + bsf = bst2->fineTrack (pCenter, bsini->getSupportVector(), + scanwidth2, bswidth, gRef); + + if (bsf == NULL || bsf->size () < bsMinSize) + { + resultValue = (bsf == NULL ? RESULT_FINAL_NO_DETECTION + : RESULT_FINAL_TOO_FEW); + return; + } + + // Connected components analysis */ + //------------------------------*/ + if (ccOn) + { + int bsccp = bsf->countOfConnectedPoints (connectMinSize); + int bssize = bsf->getAllPoints().size (); + if (bsccp < bssize / 2) + { + resultValue = RESULT_FINAL_TOO_SPARSE; + delete bsf; + bsf = NULL; + return; + } + } + + // Line space filtering + //--------------------- + if (filteringOn) + { + BlurredSegment *fbsf = lsf2->filter (bsf); + if (fbsf != NULL) + { + resultValue = RESULT_FINAL_TOO_MANY_OUTLIERS; + delete bsf; + bsf = fbsf; + } + } + + resultValue = RESULT_OK; +} + + + +void BSDetector::switchOrthoScans () +{ + bst1->switchOrthoScans (); + bst2->switchOrthoScans (); +} + + +vector<Pt2i> BSDetector::getRejected (int step) const +{ + vector<Pt2i> res; + if (step != 0) + { + if (filteringOn) res = lsf2->getAccepted (); + } + else if (prefilteringOn) res = lsf1->getAccepted (); + return res; +} + + +void BSDetector::switchFiltering (int step) +{ + if (step != 0) + { + filteringOn = ! filteringOn; + if (filteringOn && lsf2 == NULL) lsf2 = new LineSpaceFilter (); + } + else + { + prefilteringOn = ! prefilteringOn; + if (prefilteringOn && lsf1 == NULL) lsf1 = new LineSpaceFilter (); + } +} + + +bool BSDetector::incConnectedComponentMinSize (bool increase) +{ + if ((! increase) && connectMinSize <= 1) return false; + connectMinSize += (increase ? 1 : -1); + return true; +} diff --git a/Code/Seg/BlurredSegment/bsdetector.h b/Code/Seg/BlurredSegment/bsdetector.h new file mode 100755 index 0000000000000000000000000000000000000000..f30bc183d9682c48b4dfa65c3c88c868473b41b7 --- /dev/null +++ b/Code/Seg/BlurredSegment/bsdetector.h @@ -0,0 +1,453 @@ +#ifndef BLURRED_SEGMENT_DETECTOR_H +#define BLURRED_SEGMENT_DETECTOR_H + +#include "bstracker.h" +#include "linespacefilter.h" + +using namespace std; + + +/** + * @class BSDetector bsdetector.h + * \brief Blurred segment detector in grey level images. + * \author {P. Even} + */ +class BSDetector +{ + +public: + + /** Extraction result : successful extraction. */ + static const int RESULT_UNDETERMINED; + /** Extraction result : successful extraction. */ + static const int RESULT_OK; + /** Extraction result : no initial detection (bsini == NULL). */ + static const int RESULT_INITIAL_NO_DETECTION; + /** Extraction result : too few points at initial detection. */ + static const int RESULT_INITIAL_TOO_FEW; + /** Extraction result : unsuccessful density test at initial detection. */ + static const int RESULT_INITIAL_TOO_SPARSE; + /** Extraction result : unsuccessful filter test at initial detection. */ + static const int RESULT_INITIAL_TOO_MANY_OUTLIERS; + /** Extraction result : no final detection (bsf == NULL). */ + static const int RESULT_FINAL_NO_DETECTION; + /** Extraction result : too few points at final detection. */ + static const int RESULT_FINAL_TOO_FEW; + /** Extraction result : unsuccessful connectivity test at final detection. */ + static const int RESULT_FINAL_TOO_SPARSE; + /** Extraction result : unsuccessful filter test at final detection. */ + static const int RESULT_FINAL_TOO_MANY_OUTLIERS; + + + /** + * \brief Creates a segment detector. + */ + BSDetector (); + + /** + * \brief Deletes the segment detector. + */ + ~BSDetector (); + + /** + * \brief Returns the minimal vertical or horizontal width. + */ + inline int result () const { return resultValue; } + + /** + * \brief Sets the gradient map. + */ + void setGradientMap (VMap *data); + + /** + * \brief Detects all blurred segments in the picture. + */ + void detectAll (); + + /** + * \brief Detects all blurred segments between two input points. + * @param p1 First input point. + * @param p2 Second input point. + */ + void multidetect (const Pt2i &p1, const Pt2i &p2); + + /** + * \brief Detects a blurred segment between two input points. + * Step 1: For each scan line, one candidate is selected + * based on the gradient norm only (no direction test). + * Step 2: Outliers pruning based on parameter space pre-filtering. + * Step 3: For each scan line, local candidates are detected + * on top of gradient ridges with closest direction. + * The first candidates that prolongates the segment are retained. + * Step 4: Outliers pruning based on parameter space filtering. + * Note : Multi-detection along a stroke requires an initial start point. + * @param p1 First input point. + * @param p2 Second input point. + * @param p0 Initial segment start point (if different from NULL). + */ + void detect (const Pt2i &p1, const Pt2i &p2, Pt2i *p0 = NULL); + + /** + * \brief Returns the last detected blurred segment at given step. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline BlurredSegment *getBlurredSegment (int step) { + return (step == 0 ? bsini : bsf); } + + /** + * \brief Returns the list of detected blurred segments. + */ + inline vector<BlurredSegment *> getBlurredSegments () { return (mbsf); } + + /** + * \brief Avoids the deletion of the last extracted blurred segent. + */ + inline void preserveFormerBlurredSegment () { tempo = false; } + + /** + * \brief Returns the input maximal width of the initial segment. + */ + inline int getInputMaxWidth () const { return bst1->inputMaxWidth (); } + + /** + * \brief Sets the maximal width of the initial segment. + */ + inline void setInputMaxWidth (int value) { + bst1->setInputMaxWidth (value); } + + /** + * \brief Returns the output blurred segment minimal size. + */ + inline int getBSminSize () const { return bsMinSize; } + + /** + * \brief Sets the output blurred segment minimal size. + */ + inline void setBSminSize (int value) { + if (value >= ABSOLUTE_BS_MIN_SIZE) bsMinSize = value; } + + /** + * \brief Returns the automatic detection grid resolution. + */ + inline int getAutoGridResolution () const { return autoResol; } + + /** + * \brief Sets the automatic detection grid resolution. + */ + inline void setAutoGridResolution (int number) { + if (number > 0 && number < gMap->getWidth () / 8) autoResol = number; } + + /** + * \brief Returns the pixel lack tolerence. + */ + inline int getPixelLackTolerence () const { + return bst2->pixelLackTolerence (); } + + /** + * \brief Sets the pixel lack tolerence. + */ + inline void setPixelLackTolerence (int number) { + bst2->setPixelLackTolerence (number); } + + /** + * \brief Returns the count of accepted points to release a failure. + */ + inline int getRestartOnLack () { return bst2->getRestartOnLack (); } + + /** + * \brief Switches on or off the automatic restart after failure. + */ + inline void switchAutoRestart () { bst2->switchAutoRestart (); } + + /** + * \brief Returns the edge direction constraint status. + * +1 : Edge direction constrained to the initial direction. + * -1 : Edge direction constrained to the initial direction opposite. + * 0 : Edge constraint free (detects lines as well as edges). + */ + inline int edgeDirectionConstraint () + { + return (edgeDirection); + } + + /** + * \brief Inverts the edge direction for detection stage. + */ + inline void invertEdgeDirection () + { + edgeDirection = - edgeDirection; + } + + /** + * \brief Switches the edge direction constraint. + */ + inline void switchEdgeDirectionConstraint () + { + edgeDirection = (edgeDirection == 0 ? 1 : 0); + gMap->switchOrientationConstraint (); + } + + /** + * \brief Returns true if the automatic width selection is set. + */ + inline bool autoWidthOn () { return autowidth; } + + /** + * \brief Switches on or off the automatic width selection. + */ + inline void switchAutoWidth () { autowidth = ! autowidth; } + + /** + * \brief Returns true if the multi-selection modality is set. + */ + inline bool isMultiSelection () { return multiSelection; } + + /** + * \brief Switches on or off the multi-selection modality. + */ + inline void switchMultiSelection () { multiSelection = ! multiSelection; } + + /** + * \brief Returns the upper bound of the final scan of the given step. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline vector<Pt2i> getScanBound1 (int step) const { + return (step != 0 ? bst2->getScanBound1 () : bst1->getScanBound1 ()); } + + /** + * \brief Returns the lower bound of the final scan of the given step. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline vector<Pt2i> getScanBound2 (int step) const { + return (step != 0 ? bst2->getScanBound2 () : bst1->getScanBound2 ()); } + + /** + * \brief Returns the scan lines of the given step. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline vector <vector <Pt2i> > getScans (int step) const { + return (step != 0 ? bst2->getScans () : bst1->getScans ()); } + + /** + * \brief Returns whether the scan record modality is set for the given step. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline bool scanRecordOn (int step) { + return (step != 0 ? bst2->scanRecordOn () : bst1->scanRecordOn ()); } + + /** + * \brief Sets the scan record modality of initial or final step. + * @param step Initial step addressed if set to 0, final step otherwise. + * @param status Sets on if true, off otherwise. + */ + inline void setScanRecord (int step, bool status) { + if (step != 0) bst2->setScanRecord (status); + else bst1->setScanRecord (status); } + + /** + * \brief Toggles the initial step bounding. + */ + inline void switchInitialBounding () { bst1->switchScanExtent (); } + + /** + * \brief Returns the maximal scan extent of the initial detection. + */ + inline int initialDetectionMaxExtent () { return (bst1->maxScanExtent ()); } + + /** + * \brief Returns if the dynamic scans modality is set. + */ + inline bool dynamicScansOn () { return bst2->dynamicScansOn (); } + + /** + * \brief Switches on or off the dynamic scans modality. + */ + inline void switchDynamicScans () { bst2->toggleDynamicScans (); } + + /** + * \brief Sets the dynamic scans modality on or off. + */ + inline void setDynamicScans (bool onOff) { bst2->setDynamicScans (onOff); } + + /** + * \brief Returns if the orthographic scans modality set. + */ + inline bool orthoScansOn () { return bst1->orthoScansOn (); } + + /** + * \brief Switches on or off the orthogonal scans modality. + */ + void switchOrthoScans (); + + /** + * \brief Returns if the thinning is activated. + */ + inline bool isThinningActivated () { return bst2->isThinningActivated (); } + + /** + * \brief Toggles the thinning strategy. + */ + inline void toggleThinning () { bst2->toggleThinning (); } + + /** + * \brief Returns if one of the filter is activated. + */ + inline bool isOneFilterSet () { return (filteringOn || prefilteringOn); } + + /** + * \brief Returns the line space based filter. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline LineSpaceFilter *getFilter (int step) const { + return (step != 0 ? lsf2 : lsf1); } + + /** + * \brief Returns the accepted points by the line space based filter. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline vector<Pt2i> getAccepted (int step) const { + return (step != 0 ? + (filteringOn ? lsf2->getAccepted () : bsf->getAllPoints ()) : + (prefilteringOn ? lsf1->getAccepted () : bsini->getAllPoints ())); } + + /** + * \brief Returns the rejected points by the line space based filter. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + vector<Pt2i> getRejected (int step) const; + + /** + * \brief Returns if the given line space based filter is activated. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + inline bool isFiltering (int step) const { + return (step != 0 ? filteringOn : prefilteringOn); } + + /** + * \brief Toggles the use of the given line space based filter. + * @param step Initial step addressed if set to 0, final step otherwise. + */ + void switchFiltering (int step); + + /** + * \brief Returns the blurred segment size before pre-filtering. + */ + inline int prefilteringInputSize () { + return (prefilteringOn ? lsf1->blurredSegmentInitialSize () : 0); } + + /** + * \brief Returns the blurred segment size after pre-filtering. + */ + inline int prefilteringOutputSize () { + return (prefilteringOn ? lsf1->blurredSegmentFinalSize () : 0); } + + /** + * \brief Returns whether the density test at initial step is set. + */ + inline bool isSetDensityTest () { return (densityTestOn); } + + /** + * \brief Switches on or off the density test modality. + */ + inline void switchDensityTest () { densityTestOn = ! densityTestOn; } + + /** + * \brief Switches on or off the connectivity constraint. + */ + inline void switchConnectivityConstraint () { ccOn = ! ccOn; } + + /** + * \brief Returns the connectivity constraint status. + */ + inline bool isSetConnectivityConstraint () { return (ccOn); } + + /** + * \brief Returns the minimal size of the segment connected components. + */ + inline int connectedComponentMinSize () { return connectMinSize; } + + /** + * \brief Increments the minimal size of the connected components. + * @param increase Positive increment if true, negative otherwise. + */ + bool incConnectedComponentMinSize (bool increase); + + +private : + + /** Default value for the minimal size of the detected blurred segment. */ + static const int DEFAULT_BS_MIN_SIZE; + /** Absolute value for the minimal size of the detected blurred segment. */ + static const int ABSOLUTE_BS_MIN_SIZE; + /** Default value for the minimal size of connected components. */ + static const int DEFAULT_CONNECT_MIN_SIZE; + /** Default value for the automatic detection grid resolution. */ + static const int DEFAULT_AUTO_RESOLUTION; + + + /** Direction constraint of the detected edge : + * +1 : complies to initial direction + * -1 : complies to initial direction opposite + * 0 : no direction condition (detects lines as well as edges) + */ + int edgeDirection; + /** Minimal length of scan lines. */ + int minScanLength; + /** Connectivity constraint status. */ + bool ccOn; + /** Minimal size of the detected blurred segment. */ + int bsMinSize; + /** Minimal size of the connected components to validate a blurred segment. */ + int connectMinSize; + /** Automatic width selection (based on previous detection). */ + bool autowidth; + /** Density test modality after initial detection. */ + bool densityTestOn; + /** Segment multi-selection modality status. */ + bool multiSelection; + /** Grid resolution for the automatic extraction. */ + int autoResol; + + + /** Gradient map. */ + VMap *gMap; + + /** Initially detected blurred segment (initial step result). */ + BlurredSegment *bsini; + /** Initial rough tracker */ + BSTracker *bst1; + /** Flag indicating whether the blurred segment is not registred. */ + bool tempo; + + /** Fine tracker */ + BSTracker *bst2; + /** Detected blurred segment (final result). */ + BlurredSegment *bsf; + /** Detected blurred segments (in case of multi-detection). */ + vector<BlurredSegment *> mbsf; + + /** Initial segment filtering modality. */ + bool prefilteringOn; + /** Line space based pre-filter. */ + LineSpaceFilter *lsf1; + + /** Detected segment filtering modality. */ + bool filteringOn; + /** Line space based post-filter. */ + LineSpaceFilter *lsf2; + + /** Result of the blurred segment extraction */ + int resultValue; + + + /** + * \brief Detects all blurred segments between two input points. + * @param p1 First input point. + * @param p2 Second input point. + * @param dir Direction of the selection (factored P1P2). + */ + void runMultiDetection (const Pt2i &p1, const Pt2i &p2, + const Vr2i &dir); + +}; +#endif diff --git a/Code/Seg/BlurredSegment/bstracker.cpp b/Code/Seg/BlurredSegment/bstracker.cpp new file mode 100755 index 0000000000000000000000000000000000000000..e1c5991b4d2b9322a3541680d202c21b070e4a18 --- /dev/null +++ b/Code/Seg/BlurredSegment/bstracker.cpp @@ -0,0 +1,412 @@ +#include "bstracker.h" +#include "blurredsegmentproto.h" + + + +const int BSTracker::DEFAULT_MAX_WIDTH = 5; +const int BSTracker::DEFAULT_ACCEPTED_LACKS = 5; +const int BSTracker::DEFAULT_PROXIMITY_THRESHOLD = 4; +const int BSTracker::MIN_SCAN = 8; +const int BSTracker::DEFAULT_MAX_SCAN = 32; +const int BSTracker::DEFAULT_FITTING_DELAY = 20; + +const int BSTracker::DEFAULT_THINNING_DELAY = 20; +const int BSTracker::DEFAULT_THINNING_SPEED = 2; +const int BSTracker::DEFAULT_THINNING_REACH = 50; +const int BSTracker::DEFAULT_THINNING_RESOLUTION = 1000; + +const int BSTracker::FAILURE_NO_START = 1; +const int BSTracker::FAILURE_IMAGE_BOUND_ON_RIGHT = 2; +const int BSTracker::FAILURE_IMAGE_BOUND_ON_LEFT = 4; +const int BSTracker::FAILURE_LOST_ORIENTATION = 32; + + + +BSTracker::BSTracker () +{ + proxThreshold = DEFAULT_PROXIMITY_THRESHOLD; + maxWidth = DEFAULT_MAX_WIDTH; + acceptedLacks = DEFAULT_ACCEPTED_LACKS; + minRestart = acceptedLacks; + + dynamicScans = true; + fittingDelay = DEFAULT_FITTING_DELAY; + recordScans = false; + orthoScan = false; + + thinningOn = true; + thinningDelay = DEFAULT_THINNING_DELAY; + thinningSpeed.set (DEFAULT_THINNING_SPEED, 100); + thinningReach.set (100 + DEFAULT_THINNING_REACH, 100); + gMap = NULL; + + maxScan = DEFAULT_MAX_SCAN; + + fail = 0; + + cand = new int[1]; // to avoid systematic tests + lscan = 0; + rscan = 0; +} + + +BSTracker::~BSTracker () +{ + delete cand; +} + + +void BSTracker::clear () +{ + scanBound1.clear (); + scanBound2.clear (); + scanLine.clear (); +} + + +void BSTracker::setGradientMap (VMap *data) +{ + gMap = data; + delete cand; + cand = new int[data->getHeightWidthMax ()]; +} + + +BlurredSegment *BSTracker::fastTrack (const Pt2i &p1, const Pt2i &p2, + Pt2i *p0) +{ + // Creates the scanner + DirectionalScanner *ds = NULL; + if (p0 != NULL) + ds = scanp.getScanner (*p0, p1.vectorTo (p2), 4 * maxWidth, false, + 0, 0, gMap->getWidth (), gMap->getHeight ()); + else ds = scanp.getScanner (p1, p2, + 0, 0, gMap->getWidth (), gMap->getHeight ()); + if (ds == NULL) return NULL; + + // Builds a BS builder around a central point + vector<Pt2i> pix; + if (ds->first (pix) < MIN_SCAN) + { + delete ds; + return NULL; + } + + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + int candide; + Pt2i pfirst; + if (p0 != NULL) pfirst.set (p0->x (), p0->y ()); + else + { + candide = gMap->largestIn (pix); + if (candide == -1) + { + delete ds; + return NULL; + } + pfirst.set (pix.at (candide)); + } + + BlurredSegmentProto bs (maxWidth, pfirst); + Pt2i lastLeft (pfirst); + Pt2i lastRight (pfirst); + + // Extends the segment + int lstop = 0; + int rstop = 0; + int lstart = 0; + int rstart = 0; + bool added = false; + bool scanningRight = true; + bool scanningLeft = true; + int fsCount = maxScan; + + while ((scanningRight || scanningLeft) && (fsCount--)) + { + // Extends on right + if (scanningRight) + { + if (ds->nextOnRight (pix) < MIN_SCAN) scanningRight = false; + else + { + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + added = false; + candide = gMap->largestIn (pix); + if (candide != -1) + { + if (lastRight.manhattan (pix.at (candide)) <= proxThreshold) + added = bs.addRight (pix.at (candide)); + } + if (added) + { + lastRight = pix.at (candide); + if (rstop != 0) rstop = 0; + } + else if (++rstop > acceptedLacks) + scanningRight = false; + } + } + // Extends on left + if (scanningLeft) + { + if (ds->nextOnLeft (pix) < MIN_SCAN) scanningLeft = false; + else + { + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + added = false; + candide = gMap->largestIn (pix); + if (candide != -1) + { + if (lastLeft.manhattan (pix.at (candide)) < proxThreshold) + added = bs.addLeft (pix.at (candide)); + } + if (added) + { + lastLeft = pix.at (candide); + if (lstop != 0) lstop = 0; + } + else if (++lstop > acceptedLacks) + scanningLeft = false; + } + } + } + if (rstart) bs.removeRight (rstart); + if (lstart) bs.removeLeft (lstart); + delete ds; + return (bs.endOfBirth ()); +} + + + +BlurredSegment *BSTracker::fineTrack (const Pt2i ¢er, const Vr2i &scandir, + int scanwidth2, int bswidth, + const Vr2i &gref) +{ + // Gets detected segment normal vector + Vr2i normal = scandir.orthog (); + if (! normal.orientedAs (gref)) normal.invert (); + + fail = 0; + + // Creation of the directional scanner and the array of candidates + DirectionalScanner *ds = scanp.getScanner ( + center, normal, scanwidth2, dynamicScans, + 0, 0, gMap->getWidth (), gMap->getHeight ()); + if (ds == NULL) + { + fail = FAILURE_NO_START; + return NULL; + } + + // Looking for a central point + vector<Pt2i> pix; + if (ds->first (pix) < MIN_SCAN) + { + delete ds; + fail = FAILURE_NO_START; + return NULL; + } + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + int nbc = gMap->localMax (cand, pix, normal); + if (nbc == 0) + { + delete ds; + fail = FAILURE_NO_START; + return NULL; + } + + BlurredSegmentProto bs (bswidth, pix[cand[0]]); + + // Handles thinning + int count = 0; + AbsRat maxw (bswidth * DEFAULT_THINNING_RESOLUTION, + DEFAULT_THINNING_RESOLUTION); + + // Extends the segment + lscan = 0; + rscan = 0; + int lstop = 0; + int rstop = 0; + int lstart = 0; + int rstart = 0; + bool added = false; + bool scanningRight = true; + bool scanningLeft = true; + bool thon = thinningOn; + + while (scanningRight || scanningLeft) + { + count ++; + + // Handles thinning + if (thon) + { + if (count > thinningDelay) + { + AbsRat sw = bs.segmentRationalWidth (); + AbsRat oldmaxw (maxw); + maxw.attractsTo (sw, thinningSpeed); + sw.mult (thinningReach); + if (maxw.lessThan (sw)) + { + maxw.sticksTo (sw); + if (oldmaxw.lessThan (maxw)) maxw.set (oldmaxw); + thon = false; // thinning deactivation + } + bs.setMaxWidth (maxw); + } + } + + // Resets the scan stripe + if (dynamicScans && count > fittingDelay && bs.getLine () != NULL) + { + // Stops the detection if the segment gets crosswise + if (count == fittingDelay + 1) + { + Vr2i dirn = bs.getSupportVector (); + if (4 * dirn.squaredScalarProduct (scandir) + < dirn.norm2 () * scandir.norm2 ()) + { + scanningLeft = false; + scanningRight = false; + fail += FAILURE_LOST_ORIENTATION; + } + } + int ppa, ppb, ppc; + bs.getLine()->getMedialAxis (ppa, ppb, ppc); + ds->bindTo (ppa, ppb, ppc); + } + + // Extends on right + if (scanningRight) + { + added = false; + if (ds->nextOnRight (pix) < MIN_SCAN) + { + fail += FAILURE_IMAGE_BOUND_ON_RIGHT; + scanningRight = false; + } + else + { + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + added = false; + nbc = gMap->localMax (cand, pix, normal); + for (int i = 0; ! added && i < nbc; i++) + added = bs.addRight (pix[cand[i]]); + if (added) + { + rscan = count; + if (rstop == 0) rstart = 0; + else + { + rstart ++; + //if (rstart >= minRestart || rstart >= rstop) + if (rstart >= rstop) + { + rstop = 0; + rstart = 0; + } + } + } + else + { + if (++rstop - rstart > acceptedLacks) + { + if (bs.size () <= 3) fail = FAILURE_NO_START; + scanningRight = false; + } + } + } + } + + // Extends on left + if (scanningLeft) + { + if (ds->nextOnLeft (pix) < MIN_SCAN) + { + fail += FAILURE_IMAGE_BOUND_ON_LEFT; + scanningLeft = false; + } + else + { + if (recordScans) + { + scanBound1.push_back (pix.front ()); + scanBound2.push_back (pix.back ()); + scanLine.push_back (pix); + } + added = false; + nbc = gMap->localMax (cand, pix, normal); + for (int i = 0; ! added && i < nbc; i++) + added = bs.addLeft (pix[cand[i]]); + if (added) + { + lscan = count; + if (lstop == 0) lstart = 0; + else + { + lstart ++; + //if (lstart >= minRestart || lstart >= lstop) + if (lstart >= lstop) + { + lstop = 0; + lstart = 0; + } + } + } + else + { + if (++lstop - lstart > acceptedLacks) + { + if (bs.size () <= 3) fail = FAILURE_NO_START; + scanningLeft = false; + } + } + } + } + } + if (rstart) bs.removeRight (rstart); + if (lstart) bs.removeLeft (lstart); + delete ds; + return (bs.endOfBirth ()); +} + + +void BSTracker::switchOrthoScans () +{ + orthoScan = ! orthoScan; + scanp.setOrtho (orthoScan); + cout << "OrthoScan is " << (orthoScan ? "on" : "off") << endl; +} + + +void BSTracker::switchScanExtent () +{ + maxScan = (maxScan == gMap->getHeightWidthMax () ? + DEFAULT_MAX_SCAN : gMap->getHeightWidthMax ()); +} diff --git a/Code/Seg/BlurredSegment/bstracker.h b/Code/Seg/BlurredSegment/bstracker.h new file mode 100755 index 0000000000000000000000000000000000000000..9d1aeded1ab8688b276bcd1b8c169c3442432231 --- /dev/null +++ b/Code/Seg/BlurredSegment/bstracker.h @@ -0,0 +1,282 @@ +#ifndef BLURRED_SEGMENT_TRACKER_H +#define BLURRED_SEGMENT_TRACKER_H + +#include <vector> +#include "scannerprovider.h" +#include "blurredsegment.h" +#include "vmap.h" + +using namespace std; + + +/** + * @class BSTracker bstracker.h + * \brief Blurred segment tracker in grey level images. + * \author {P. Even and B. Kerautret} + */ +class BSTracker +{ + +public: + + /** + * \brief Creates a blurred segment tracker. + */ + BSTracker (); + + /** + * \brief Deletes the blurred segment tracker. + */ + ~BSTracker (); + + /** + * \brief Clears off stored information. + */ + void clear (); + + /** + * \brief Builds and returns a blurred segment from only gradient maximum. + * @param p1 Initial stroke start point. + * @param p2 Initial stroke end point. + * @param p0 Initial segment start point (if different from NULL). + */ + BlurredSegment *fastTrack (const Pt2i &p1, const Pt2i &p2, + Pt2i *p0 = NULL); + + /** + * \brief Builds and returns a blurred segment from local gradient maxima. + * Finer detection using gradient ridges and direction input. + * @param center Central point of the scan. + * @param scandir Scan direction + * @param scanwidth2 Half width of the scan strip. + * @param bswidth Initial maximal width of the blurred segment to build. + * @param gref Gradient vector reference to select candidates. + */ + BlurredSegment *fineTrack (const Pt2i ¢er, const Vr2i &scandir, + int scanwidth2, int bswidth, + const Vr2i &gref); + + /** + * \brief Returns the input maximal width of the blurred segment. + */ + inline int inputMaxWidth () const { return maxWidth; } + + /** + * \brief Sets the input maximal width of the blurred segment. + */ + inline void setInputMaxWidth (int value) { + if (value > 0) maxWidth = value; } + + /** + * \brief Returns the default minimal length of scan lines. + */ + inline int defaultMinScan () const { return MIN_SCAN; } + + /** + * \brief Returns the pixel lack tolerence for exdending the blurred segment.. + */ + inline int pixelLackTolerence () const { return acceptedLacks; } + + /** + * \brief Sets the pixel lack tolerence for extending the blurred segment. + */ + inline void setPixelLackTolerence (int number) + { + if (number >= 0) + { + acceptedLacks = number; + if (minRestart != 1) minRestart = number; + } + } + + /** + * \brief Returns the count of accepted points to release a failure. + */ + inline int getRestartOnLack () { return minRestart; } + + /** + * \brief Switches on or off the automatic restart after failure. + */ + inline void switchAutoRestart () + { + minRestart = (minRestart == 1 ? acceptedLacks : 1); + } + + /** + * \brief Sets the image data. + */ + void setGradientMap (VMap *data); + + /** + * \brief Returns the proximity threshold. + */ + inline int proximityThreshold () { return proxThreshold; } + + /** + * \brief Returns if the dynamic scans are used. + */ + inline bool dynamicScansOn () { return dynamicScans; } + + /** + * \brief Returns the upper bound of the final scan. + */ + inline vector<Pt2i> getScanBound1 () const { return scanBound1; } + + /** + * \brief Returns the lower bound of the final scan. + */ + inline vector<Pt2i> getScanBound2 () const { return scanBound2; } + + /** + * \brief Returns the scan lines. + */ + inline vector <vector <Pt2i> > getScans () const { return scanLine; } + + /** + * \brief Returns whether the scan record modality is set. + */ + inline bool scanRecordOn () { return recordScans; } + + /** + * \brief Sets the scan record modality. + * @param status Sets on if true, off otherwise. + */ + inline void setScanRecord (bool status) { recordScans = status; } + + /** + * \brief Toggles the dynamic scans use. + */ + inline void toggleDynamicScans () { dynamicScans = ! dynamicScans; } + + /** + * \brief Switches the scan extent limitation.. + */ + void switchScanExtent (); + + /** + * \brief Returns the scan extent limit. + */ + inline int maxScanExtent () { return (maxScan); } + + /** + * \brief Sets the dynamic scans on or off. + */ + inline void setDynamicScans (bool onOff) { dynamicScans = onOff; } + + /** + * \brief Returns if the thinning is activated. + */ + inline bool isThinningActivated () { return thinningOn; } + + /** + * \brief Toggles the thinning strategy. + */ + inline void toggleThinning () { thinningOn = ! thinningOn; } + + /** + * \brief Switches on or off the orthogonal scanning modality. + */ + void switchOrthoScans (); + + /** + * \brief Switches on or off the orthogonal scanning modality. + */ + inline bool orthoScansOn () { return orthoScan; } + + +private : + + // Segment detection default parameters. + /** Default value for the initial max segment width. */ + static const int DEFAULT_MAX_WIDTH; + /** Default value forr the accepted number of successive lacks. */ + static const int DEFAULT_ACCEPTED_LACKS; + /** Default value for the proximity threshold used for fast tracking. */ + static const int DEFAULT_PROXIMITY_THRESHOLD; + /** Minimal length of scan length to be processed. */ + static const int MIN_SCAN; + /** Default value for the maximal number of scans processed on each side. */ + static const int DEFAULT_MAX_SCAN; + + // Dynamical scan default parameters. + /* Count of points before activating the fitting on the detected segment. */ + static const int DEFAULT_FITTING_DELAY; + + // Width thinning default parameters. + /* Count of points before activating the width thinning. */ + static const int DEFAULT_THINNING_DELAY; + /** Value which controls the width thinning speed. + * It is the percentage of width offset withdrawn from the width tolerence. + * Width offset = width tolerence - blurred segment width. */ + static const int DEFAULT_THINNING_SPEED; + /** Minimal value of the percentage of width offset wrt segment width. */ + static const int DEFAULT_THINNING_REACH; + /** Resolution of the max segment width when thinning. */ + static const int DEFAULT_THINNING_RESOLUTION; + + /** Segment stop information : no start point found. */ + static const int FAILURE_NO_START; + /** Segment stop information : image bound reached on the right. */ + static const int FAILURE_IMAGE_BOUND_ON_RIGHT; + /** Segment stop information : image bound reached on the left. */ + static const int FAILURE_IMAGE_BOUND_ON_LEFT; + /** Segment stop information : lost orientation at dynamical reset start. */ + static const int FAILURE_LOST_ORIENTATION; + + + /** Blurred segment max half-width. */ + int maxWidth; + /** Number of awaited points after each failure. */ + int minRestart; + /** Accepted number of successive lacks (wrt restart points). */ + int acceptedLacks; + /** Proximity threshold used for fast tracking. */ + int proxThreshold; + + /** Orthogonal scanning modality status */ + bool orthoScan; + /** Kind of scans used : dynamic if true. */ + bool dynamicScans; + /** Minimal detection width before activating the dynamical scans. */ + int fittingDelay; + + /** Segment thinning strategy. */ + bool thinningOn; + /** Width thinning delay. */ + int thinningDelay; + /** Width thinning speed : amount of thinning offset withdrawn. */ + AbsRat thinningSpeed; + /** Minimal control width wrt detected segment width when thinning. */ + AbsRat thinningReach; + + /** Gradient map. */ + VMap *gMap; + + /** Maximum number of scans. */ + int maxScan; + + /** Failure cause. */ + int fail; + + /** Candidates array for internal use. */ + int *cand; + /** Index of the last successful scan on right side. */ + int rscan; + /** Index of the last successful scan on left side. */ + int lscan; + + + /** Directional scanner provider. + * Automatically selects the appropriate octant. */ + ScannerProvider scanp; + /** Upper bound of the scan. */ + vector<Pt2i> scanBound1; + /** Lower bound of the scan. */ + vector<Pt2i> scanBound2; + /** Scan lines. */ + vector <vector <Pt2i> > scanLine; + /** Dynamical scanner record modality. */ + bool recordScans; + +}; +#endif diff --git a/Code/Seg/BlurredSegment/linespacefilter.cpp b/Code/Seg/BlurredSegment/linespacefilter.cpp new file mode 100755 index 0000000000000000000000000000000000000000..2745c38396846d385cb1c615955e050a0e9450a8 --- /dev/null +++ b/Code/Seg/BlurredSegment/linespacefilter.cpp @@ -0,0 +1,688 @@ +#include <cmath> +#include "linespacefilter.h" +#include "blurredsegmentproto.h" + + + +const int LineSpaceFilter::DEFAULT_NB_ANG_CELLS = 32; +const int LineSpaceFilter::DEFAULT_NB_DIST_CELLS = 32; +const int LineSpaceFilter::MIN_NB_ANG_CELLS = 8; +const int LineSpaceFilter::MAX_NB_ANG_CELLS = 64; +const int LineSpaceFilter::MIN_NB_DIST_CELLS = 8; +const int LineSpaceFilter::MAX_NB_DIST_CELLS = 64; +const int LineSpaceFilter::DEFAULT_SELECTION_THRESHOLD = 75; +const int LineSpaceFilter::MIN_SELECTION_THRESHOLD = 20; +const int LineSpaceFilter::MAX_SELECTION_THRESHOLD = 90; +const int LineSpaceFilter::INC_SELECTION_THRESHOLD = 5; +const int LineSpaceFilter::NO_SUBPIXEL = 1; +const int LineSpaceFilter::MAX_SUBPIXEL = 64; +const int LineSpaceFilter::ANG_DEF_ZOOM = 8; +const int LineSpaceFilter::ANG_MIN_ZOOM = 2; +const int LineSpaceFilter::ANG_MAX_ZOOM = 32; +const int LineSpaceFilter::DIST_DEF_ZOOM = 4; +const int LineSpaceFilter::DIST_MIN_ZOOM = 1; +const int LineSpaceFilter::DIST_MAX_ZOOM = 16; + + +LineSpaceFilter::LineSpaceFilter () +{ + nt = DEFAULT_NB_ANG_CELLS; + nr = DEFAULT_NB_DIST_CELLS; + acc = new int[nt*nr]; + ncos = new double[nt]; + nsin = new double[nt]; + mask = new bool[nt*nr]; + cellMax = -1; + minsel = DEFAULT_SELECTION_THRESHOLD; + azoom = ANG_DEF_ZOOM; + dzoom = DIST_DEF_ZOOM; + nbSubpix = NO_SUBPIXEL; +} + + +LineSpaceFilter::~LineSpaceFilter () +{ + delete [] acc; + delete [] ncos; + delete [] nsin; + delete [] mask; +} + + +void LineSpaceFilter::clear () +{ + leftIn.clear (); + rightIn.clear (); + out.clear (); +} + + +BlurredSegment *LineSpaceFilter::filter (BlurredSegment *bs) +{ + bsInitialSize = 0; + bsFinalSize = 0; + if (bs == NULL || ! bs->isThick ()) return NULL; + bsInitialSize = bs->size (); + if (bsInitialSize < 10) return NULL; + + // Gets the line spacefilter + const Pt2i bsstart = bs->getCenter (); + const vector<Pt2i> *bslineleft = bs->getLeftLine (); + const vector<Pt2i> *bsleft = bs->getLeftPoints (); + const vector<Pt2i> *bslineright = bs->getRightLine (); + const vector<Pt2i> *bsright = bs->getRightPoints (); + Vr2i sv = bs->getSupportVector (); + AbsRat segwidth = bs->segmentRationalWidth (); + Vr2i bbs = bs->boundingBoxSize (); + init (bsstart.x (), bsstart.y (), -sv.y (), sv.x (), + sqrt (bbs.x () * bbs.x () + bbs.y () * bbs.y ()), + segwidth.numerator () / (double) segwidth.denominator ()); + vote (bsstart); + for (vector<Pt2i>::const_iterator it = bslineleft->begin (); + it != bslineleft->end (); it ++) + vote (*it); + for (vector<Pt2i>::const_iterator it = bsleft->begin (); + it != bsleft->end (); it ++) + vote (*it); + for (vector<Pt2i>::const_iterator it = bslineright->begin (); + it != bslineright->end (); it ++) + vote (*it); + for (vector<Pt2i>::const_iterator it = bsright->begin (); + it != bsright->end (); it ++) + vote (*it); + findMaxThroughOrigin (); + fitMask (); + + // Filters the previous points + for (vector<Pt2i>::const_iterator it = bsleft->begin (); + it != bsleft->end (); it ++) + { + if (voteInMask (*it)) leftIn.push_back (Pt2i (*it)); + else out.push_back (Pt2i (*it)); + } + for (vector<Pt2i>::const_iterator it = bsright->begin (); + it != bsright->end (); it ++) + { + if (voteInMask (*it)) rightIn.push_back (Pt2i (*it)); + else out.push_back (Pt2i (*it)); + } + + // Returns a blurred segment with the accepted points + int mw = 1; + AbsRat sw = bs->segmentRationalWidth (); + if (sw.denominator () != 0) mw += sw.numerator () / sw.denominator (); + BlurredSegmentProto resbs (mw, bsstart, leftIn, rightIn); + bsFinalSize = resbs.size (); + return (resbs.endOfBirth ()); +} + + +void LineSpaceFilter::getAccumulator (unsigned char *map, int *solution) +{ + if (cellMax != -1) + { + int max = 0; + for (int i = 0; i < nt * nr; i++) if (acc[i] > max) max = acc[i]; + for (int i = 0; i < nt * nr; i++) + map[i] = (unsigned char) ((acc[i] * 255) / max); + solution[0] = cellMax % nt; + solution[1] = cellMax / nt; + } +} + + +bool LineSpaceFilter::resize (bool larger) +{ + bool ok = false; + if (larger) + { + if (nt > MIN_NB_ANG_CELLS) + { + nt /= 2; + nr /= 2; + ok = true; + } + } + else + { + if (nt < MAX_NB_ANG_CELLS) + { + nt *= 2; + nr *= 2; + ok = true; + } + } + if (ok) + { + delete [] acc; + delete [] ncos; + delete [] nsin; + delete [] mask; + acc = new int[nt*nr]; + ncos = new double[nt]; + nsin = new double[nt]; + mask = new bool[nt*nr]; + } + return (ok); +} + + +bool LineSpaceFilter::zoom (bool in) +{ + bool ok = false; + if (in) + { + if (dzoom > DIST_MIN_ZOOM) + { + dzoom /= 2; + azoom /= 2; + ok = true; + } + } + else + { + if (dzoom < DIST_MAX_ZOOM) + { + dzoom *= 2; + azoom *= 2; + ok = true; + } + } + return (ok); +} + + +bool LineSpaceFilter::subpixellise (bool larger) +{ + bool ok = false; + if (larger) + { + if (nbSubpix < MAX_SUBPIXEL) + { + nbSubpix *= 2; + ok = true; + } + } + else + { + if (nbSubpix > NO_SUBPIXEL) + { + nbSubpix /= 2; + ok = true; + } + } + return (ok); +} + + +bool LineSpaceFilter::setSelectivity (bool larger) +{ + bool ok = false; + if (larger) + { + if (minsel < MAX_SELECTION_THRESHOLD) + { + minsel += INC_SELECTION_THRESHOLD; + ok = true; + } + } + else + { + if (minsel > MIN_SELECTION_THRESHOLD) + { + minsel -= INC_SELECTION_THRESHOLD; + ok = true; + } + } + return (ok); +} + + +vector<Pt2i> LineSpaceFilter::getAccepted () +{ + vector<Pt2i> res; + for (vector<Pt2i>::iterator it = leftIn.begin (); + it != leftIn.end (); it ++) + res.push_back (*it); + for (vector<Pt2i>::iterator it = rightIn.begin (); + it != rightIn.end (); it ++) + res.push_back (*it); + return res; +} + + +void LineSpaceFilter::checkManhattanScan () +{ + if (cellMax == -1) findMax (); // default choice if not already done + int cell0 = cellMax; + int x0 = cell0 % nt; + int y0 = cell0 / nt; + int nrt = nr * nt; + bool poursuite = true; + int tour = 1; +int val = 1; +int vtour = 6; + + while (poursuite) + { + // bottom cell first + int cell = cell0 - nt * tour; + if (cell < 0) + { + // first cell on the diagonal inside the accumulator + cell += (nt + 1) * (1 + (-cell) / nt); + if (cell >= nt) cell = cell0; + } + while (cell < cell0) + { + // process cell HERE +if (tour == vtour) acc[cell] = val++; + cell += nt + 1; + if (cell % nt == 0) cell = cell0; + } + + // right cell next + cell = cell0 + tour; + if (cell % nt <= x0) + { + // first cell on the diagonal inside the accumulator + cell += (nt - 1) * (x0 + tour + 1 - nt); + if (cell >= nrt) cell = cell0; + } + while (cell % nt > x0) + { + // process cell HERE +if (tour == vtour) acc[cell] = val++; + cell += nt - 1; + if (cell >= nrt) cell = cell0; + } + + // top cell next + cell = cell0 + nt * tour; + if (cell >= nrt) + { + // first cell on the diagonal inside the accumulator + cell -= (nt + 1) * (y0 + tour + 1 - nr); + if (cell % nt >= x0) cell = cell0; + } + while (cell / nt > y0) + { + // process cell HERE +if (tour == vtour) acc[cell] = val++; + cell -= nt + 1; + if (cell % nt >= x0) cell = cell0; + } + + // left cell next + cell = cell0 - tour; + if (tour > x0) + { + // first cell on the diagonal inside the accumulator + cell -= (nt - 1) * (tour - x0); + if (cell < 0) cell = cell0; + } + while (cell % nt < x0) + { + // process cell HERE +if (tour == vtour) acc[cell] = val++; + cell -= nt - 1; + if (cell < 0) cell = cell0; + } + + tour ++; + poursuite = (tour != 12); + } +} + + +void LineSpaceFilter::init (double x0, double y0, int a, int b, + double length, double width) +{ + // Clears the accumulator + int *ac = acc; + bool *m = mask; + for (int i = 0; i < nt * nr; i++) + { + *ac++ = 0; + *m++ = false; + } + cellMax = -1; + + // Computes the column values (normal vector deviations) + double w = azoom * width / nt; + double hyp = sqrt (w * w + length * length); + double dcos = length / hyp; + da = acos (dcos); + double dsin = w / hyp; + double n = sqrt (a * a + b * b); + ncos[nt/2] = a / n; + nsin[nt/2] = b / n; + for (int i = nt / 2; i > 0; i--) + { + ncos[i-1] = ncos[i] * dcos + nsin[i] * dsin; + nsin[i-1] = nsin[i] * dcos - ncos[i] * dsin; + } + for (int i = nt / 2 + 1; i < nt; i++) + { + ncos[i] = ncos[i-1] * dcos - nsin[i-1] * dsin; + nsin[i] = nsin[i-1] * dcos + ncos[i-1] * dsin; + } + + // Computes the row values (distance parameter) + cx = x0; + cy = y0; + dr = dzoom * width / nr; + r0 = - ((nr / 2) + 0.5) * dr; +} + + +void LineSpaceFilter::vote (int i, int j) +{ + acc[i + j * nt] ++; +} + + +void LineSpaceFilter::vote (Pt2i pix) +{ + int x = pix.x () - cx; + int y = pix.y () - cy; + for (int i = 0; i < nt; i++) + { + if (nbSubpix == 1) + { + double d = x * ncos[i] + y * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr) acc[i + rho * nt] ++; + } + } + else + { + for (int ix = 0; ix < nbSubpix; ix ++) + for (int jy = 0; jy < nbSubpix; jy ++) + { + double d = (x + (2 * ix - 15) / 32.) * ncos[i] + + (y + (2 * jy - 15) / 32.) * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr) acc[i + rho * nt] ++; + } + } + } + } +} + + +void LineSpaceFilter::vote (const vector<Pt2i>& pts) +{ + vector<Pt2i>::const_iterator it = pts.begin (); + while (it != pts.end ()) + { + int x = it->x () - cx; + int y = it->y () - cy; + for (int i = 0; i < nt; i++) + { + if (nbSubpix == 1) + { + double d = x * ncos[i] + y * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr) acc[i + rho * nt] ++; + } + } + else + { + for (int ix = 0; ix < nbSubpix; ix ++) + for (int jy = 0; jy < nbSubpix; jy ++) + { + double d = (x + (2 * ix - 15) / 32.) * ncos[i] + + (y + (2 * jy - 15) / 32.) * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr) acc[i + rho * nt] ++; + } + } + } + } + it ++; + } +} + + +bool LineSpaceFilter::voteInMask (Pt2i pix) +{ + int x = pix.x () - cx; + int y = pix.y () - cy; + for (int i = 0; i < nt; i++) + { + if (nbSubpix == 1) + { + double d = x * ncos[i] + y * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr && mask[i + rho * nt]) return true; + } + } + else + { + for (int ix = 0; ix < nbSubpix; ix ++) + for (int jy = 0; jy < nbSubpix; jy ++) + { + double d = (x + (2 * ix - 15) / 32.) * ncos[i] + + (y + (2 * jy - 15) / 32.) * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr && mask[i + rho * nt]) return true; + } + } + } + } + return false; +} + + +vector<Pt2i> LineSpaceFilter::voteInMask (vector<Pt2i>& pts) +{ + vector<Pt2i> vpix; + vector<Pt2i>::iterator it = pts.begin (); + while (it != pts.end ()) + { + int x = it->x () - cx; + int y = it->y () - cy; + for (int i = 0; i < nt; i++) + { + double d = x * ncos[i] + y * nsin[i]; + if (d >= r0) + { + int rho = (int) ((d - r0) / dr); + if (rho < nr && mask[i + rho * nt]) vpix.push_back (*it); + } + } + it ++; + } + return (vpix); +} + + +void LineSpaceFilter::findMax () +{ + cellMax = 0; + for (int i = 1; i < nt * nr; i++) + if (acc[i] > acc[cellMax]) cellMax = i; +} + + +void LineSpaceFilter::findMaxThroughOrigin () +{ + cellMax = (nr / 2) * nt; + for (int i = 1; i < nt; i++) + if (acc[cellMax + i] > acc[cellMax]) cellMax = cellMax + i; +} + + +void LineSpaceFilter::fitMask () +{ + if (cellMax == -1) findMax (); + int cell0 = cellMax; + int x0 = cell0 % nt; + int y0 = cell0 / nt; + int nrt = nr * nt; + bool rt = true, tl = true, lb = true, br = true; + int tour = 1; + int cell; + + mask[cellMax] = true; + while (br || rt || tl || lb) + { + // bottom cell first + if (br) + { + br = false; + cell = cell0 - nt * tour; + if (cell < 0) + { + // first cell on the diagonal inside the accumulator + cell += (nt + 1) * (1 + (-cell) / nt); + if (cell >= nt) cell = cell0; + } + else + { + // process bottom cell HERE + if (mask[cell+nt] && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + br = true; + } + cell += nt + 1; + if (cell % nt == 0) cell = cell0; + } + while (cell < cell0) + { + // process cell HERE + if ((mask[cell+nt] || mask[cell-1]) + && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + br = true; + } + cell += nt + 1; + if (cell % nt == 0) cell = cell0; + } + } + + // right cell next + if (rt) + { + rt = false; + cell = cell0 + tour; + if (cell % nt <= x0) + { + // first cell on the diagonal inside the accumulator + cell += (nt - 1) * (x0 + tour + 1 - nt); + if (cell >= nrt) cell = cell0; + } + else + { + // process left cell HERE + if (mask[cell-1] && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + rt = true; + } + cell += nt - 1; + if (cell >= nrt) cell = cell0; + } + while (cell % nt > x0) + { + // process cell HERE + if ((mask[cell-1] || mask[cell-nt]) + && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + rt = true; + } + cell += nt - 1; + if (cell >= nrt) cell = cell0; + } + } + + // top cell next + if (tl) + { + tl = false; + cell = cell0 + nt * tour; + if (cell >= nrt) + { + // first cell on the diagonal inside the accumulator + cell -= (nt + 1) * (y0 + tour + 1 - nr); + if (cell % nt >= x0) cell = cell0; + } + else + { + // process top cell HERE + if (mask[cell-nt] && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + tl = true; + } + cell -= nt + 1; + if (cell % nt >= x0) cell = cell0; + } + while (cell / nt > y0) + { + // process cell HERE + if ((mask[cell-nt] || mask[cell+1]) + && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + tl = true; + } + cell -= nt + 1; + if (cell % nt >= x0) cell = cell0; + } + } + + // left cell next + if (lb) + { + lb = false; + cell = cell0 - tour; + if (tour > x0) + { + // first cell on the diagonal inside the accumulator + cell -= (nt - 1) * (tour - x0); + if (cell < 0) cell = cell0; + } + else + { + // process left cell HERE + if (mask[cell+1] && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + lb = true; + } + cell -= nt - 1; + if (cell < 0) cell = cell0; + } + while (cell % nt < x0) + { + // process cell HERE + if ((mask[cell+1] || mask[cell+nt]) + && acc[cell] > (acc[cellMax] * minsel) / 100) + { + mask[cell] = true; + lb = true; + } + cell -= nt - 1; + if (cell < 0) cell = cell0; + } + } + tour ++; + } +} diff --git a/Code/Seg/BlurredSegment/linespacefilter.h b/Code/Seg/BlurredSegment/linespacefilter.h new file mode 100755 index 0000000000000000000000000000000000000000..3c6be37fd1fc7c5dd23a8ead91a7ca19e45a4879 --- /dev/null +++ b/Code/Seg/BlurredSegment/linespacefilter.h @@ -0,0 +1,293 @@ +#ifndef LINE_SPACE_FILTER_H +#define LINE_SPACE_FILTER_H + +#include <vector> +#include "pt2i.h" +#include "blurredsegment.h" + +using namespace std; + + +/** + * @class Parameter space filter for detecting thick straight lines. + * \author {P. Even} + */ +class LineSpaceFilter +{ + +public: + + /** Default number of line space filter angle cells. */ + static const int DEFAULT_NB_ANG_CELLS; + /** Default number of line space filter distance cells. */ + static const int DEFAULT_NB_DIST_CELLS; + + + /** + * \brief Creates a empty line space. + */ + LineSpaceFilter (); + + /** + * \brief Deletes the line space. + */ + ~LineSpaceFilter (); + + /** + * \brief Clears the stored information. + */ + void clear (); + + /** \brief Filters and returns a blurred segment. + * @param bs The blurred segment to be processed. + */ + BlurredSegment *filter (BlurredSegment *bs); + + /** + * \brief Gets the filtering cells. + */ + inline bool *getMask () const + { + return mask; + } + + /** + * \brief Gets the accumulator values. + * @param map Accumulator vote cells + * @param solution Accumulator max cell coordinates + */ + void getAccumulator (unsigned char *map, int *solution); + + /** \brief Returns the number of columns of the accumulation filter. + */ + inline int width () const { return nt; } + + /** \brief Returns the number of lines of the accumulation filter. + */ + inline int height () const { return nr; } + + /** \brief Resizes the accumulation array. + * Returns true if the required modification is actually performed. + * @param larger Sets larger if true, smaller otherwise. + */ + bool resize (bool larger); + + /** + * \brief Returns the angular resolution in radians. + */ + inline double angularResolution () const + { + return da; + } + + /** \brief Returns the distance resolution in pixels. + */ + inline double distanceResolution () const + { + return dr; + } + + /** \brief Returns the distance zoom factor of the accumulator display. + */ + inline int getZoom () const + { + return (dzoom); + } + + /** \brief Zooms in or out the accumulator display. + * Returns true if the required modification is actually performed. + * @param in Zooms in if true, out otherwise. + */ + bool zoom (bool in); + + /** \brief Returns the sub-pixel resolution used for voting. + */ + inline int getSubpix () const + { + return (nbSubpix); + } + + /** \brief Modifies the subpixellic resolution for voting. + * Returns true if the required modification is actually performed. + * @param larger Sets larger if true, smaller otherwise. + */ + bool subpixellise (bool larger); + + /** \brief Returns the selection threshold (in percent). + */ + inline int getSelectionThreshold () const + { + return (minsel); + } + + /** \brief Modifies the filter selection threshold. + * Returns true if the required modification is actually performed. + * @param larger Sets larger if true, smaller otherwise. + */ + bool setSelectivity (bool larger); + + /** + * \brief Returns the accepted points by the line space filter. + */ + vector<Pt2i> getAccepted (); + + /** + * \brief Returns the rejected points by the line space filter. + */ + inline vector<Pt2i> getRejected () const { return out; } + + /** + * \brief Returns the blurred segment size before filtering. + */ + inline int blurredSegmentInitialSize () const { return bsInitialSize; } + + /** + * \brief Returns the blurred segment size after filtering. + */ + inline int blurredSegmentFinalSize () const { return bsFinalSize; } + + /** + * \brief Scans manhattan circles around the highest vote. + * Votes for one specific radius to test the scan correctness. + */ + void checkManhattanScan (); + + +private : + + /** Min number of line space filter angle cells. */ + static const int MIN_NB_ANG_CELLS; + /** Max number of line space filter angle cells. */ + static const int MAX_NB_ANG_CELLS; + /** Min number of line space filter distance cells. */ + static const int MIN_NB_DIST_CELLS; + /** Max number of line space filter distance cells. */ + static const int MAX_NB_DIST_CELLS; + /** Default filter selection threshold (in percentage). */ + static const int DEFAULT_SELECTION_THRESHOLD; + /** Minimal filter selection threshold (in percentage). */ + static const int MIN_SELECTION_THRESHOLD; + /** Maximal filter selection threshold (in percentage). */ + static const int MAX_SELECTION_THRESHOLD; + /** Increment value for the selection threshold setting. */ + static const int INC_SELECTION_THRESHOLD; + /** Default sub-pixellic resolution for voting. */ + static const int NO_SUBPIXEL; + /** Maximal sub-pixellic resolution for voting. */ + static const int MAX_SUBPIXEL; + /** Angle zoom factor default value. */ + static const int ANG_DEF_ZOOM; + /** Angle zoom factor min value. */ + static const int ANG_MIN_ZOOM; + /** Angle zoom factor max value. */ + static const int ANG_MAX_ZOOM; + /** Distance zoom factor default value. */ + static const int DIST_DEF_ZOOM; + /** Distance zoom factor min value. */ + static const int DIST_MIN_ZOOM; + /** Distance zoom factor max value. */ + static const int DIST_MAX_ZOOM; + + /** Accumulator. */ + int *acc; + /** Angle zoom factor. */ + int azoom; + /** Distance zoom factor. */ + int dzoom; + /** Subpixel resolution for voting. */ + int nbSubpix; + + /** Count of accumulator rows (angles). */ + int nt; + /** Pre-computed array of deviated normal vectors. */ + double *ncos, *nsin; + /** Angular resolution (in readians). */ + double da; + + /** Count of accumulator columns (distances). */ + int nr; + /** Distance resolution (offset between consecutive accumulator columns). */ + double dr; + /** Origin of the line space. */ + int cx, cy; + /** Distance value of the accumulator bottom line. */ + double r0; + + /** Dominant cell (-1 if not yet determined) */ + int cellMax; + /** Extraction mask used to prune outlier candidates. */ + bool *mask; + /** Filter selection ratio (in %) */ + int minsel; + + /** Blurred segment size before filtering. */ + int bsInitialSize; + /** Blurred segment size after filtering. */ + int bsFinalSize; + /** Accepted left points after line space filtering. */ + vector<Pt2i> leftIn; + /** Accepted right points after line space filtering. */ + vector<Pt2i> rightIn; + /** Rejected points after line space filtering. */ + vector<Pt2i> out; + + + /** + * \brief Clears the accumulator and centers it on a new blurred segment. + * @param x0 Segment center abscissae + * @param y0 Segment center ordinate + * @param a Segment normal vector abscissae + * @param b Segment normal vector ordinate + * @param length Segment length + * @param width Segment width + */ + void init (double x0, double y0, int a, int b, + double length, double width); + + /** + * \brief Direct vote into the accumulator for testing + */ + void vote (int i, int j); + + /** + * \brief Adds one point votes into the accumulator + * @param pix Voting point + */ + void vote (Pt2i pix); + + /** + * \brief Adds multiple points votes into the accumulator + * @param points Voting points + */ + void vote (const vector<Pt2i>& points); + + /** + * \brief Checks whether the point votes in the active part of the accumulator + * @param pix Voting point + */ + bool voteInMask (Pt2i pix); + + /** + * \brief Tests multiple points votes into the active part of the accumulator + * @param points Voting points + * Returns a vector of inlier points + */ + vector<Pt2i> voteInMask (vector<Pt2i>& points); + + /** + * \brief Determines the cell with the highest vote. + */ + void findMax (); + + /** + * \brief Determines the rho=0 cell with the highest vote. + */ + void findMaxThroughOrigin (); + + /** + * \brief Fits the extraction mask around the interest area. + */ + void fitMask (); + +}; +#endif diff --git a/Code/Seg/ConvexHull/antipodal.cpp b/Code/Seg/ConvexHull/antipodal.cpp new file mode 100755 index 0000000000000000000000000000000000000000..60b70fdceb44aefe4ac686839a42b3adca6ad4b0 --- /dev/null +++ b/Code/Seg/ConvexHull/antipodal.cpp @@ -0,0 +1,343 @@ +#include "antipodal.h" + + +Antipodal::Antipodal () +{ + ix = 0; + iy = 1; + vpt = NULL; + ept1 = NULL; + ept2 = NULL; +} + + +void Antipodal::init (CHVertex *v1, CHVertex *v2, CHVertex *v3) +{ + if (v1->get (iy) < v2->get (iy)) + { + if (v2->get (iy) < v3->get (iy)) + { + vpt = v2; + ept1 = v1; + ept2 = v3; + } + else + { + if (v1->get (iy) < v3->get (iy)) + { + vpt = v3; + ept1 = v1; + ept2 = v2; + } + else + { + vpt = v1; + ept1 = v2; + ept2 = v3; + } + } + } + else + { + if (v1->get (iy) < v3->get (iy)) + { + vpt = v1; + ept1 = v2; + ept2 = v3; + } + else + { + if (v2->get (iy) <= v3->get (iy)) // EQUIV : au lieu de "<" !!! + { + vpt = v3; + ept1 = v1; + ept2 = v2; + } + else + { + vpt = v2; + ept1 = v1; + ept2 = v3; + } + } + } +} + + +AbsRat Antipodal::rationalWidth () const +{ + int den = ept2->get (iy) - ept1->get (iy); + return (AbsRat (((vpt->get (ix) - ept1->get (ix)) * den + - (vpt->get (iy) - ept1->get (iy)) + * (ept2->get (ix) - ept1->get (ix))), den)); +} + + +void Antipodal::width (int &num, int &den) const +{ + den = ept2->get (iy) - ept1->get (iy); + if (den < 0) den = -den; + num = (vpt->get (ix) - ept1->get (ix)) * den + - (vpt->get (iy) - ept1->get (iy)) * (ept2->get (ix) - ept1->get (ix)); + if (num < 0) num = -num; +} + + +int Antipodal::remainder (CHVertex *v) const +{ + int a = ept2->y () - ept1->y (); + int b = ept2->x () - ept1->x (); + if (a == 0) return ((b > 0 ? -b : b) * v->y ()); + if (a < 0) + { + a = -a; + b = -b; + } + return (a * v->x () - b * v->y ()); +} + + +bool Antipodal::edgeInFirstQuadrant () const +{ + if (iy) return true; + int a = ept2->y () - ept1->y (); + if (a == 0) return true; + return (a > 0 ? (ept1->x () < ept2->x ()) : (ept2->x () < ept1->x ())); +} + + +int Antipodal::getA () const +{ + int a = ept2->y () - ept1->y (); + return (a < 0 ? -a : a); +} + + +int Antipodal::getB () const +{ + int a = ept2->y () - ept1->y (); + int b = ept2->x () - ept1->x (); + if (a < 0) b = -b; + else if (a == 0 && b < 0) b = -b; + return (b); +} + + +ostream& operator<< (ostream &os, const Antipodal &ap) +{ + os << (ap.ix ? "AV [" : "AH [") << *(ap.vpt) << " + (" + << *(ap.ept1) << " - " << *(ap.ept2) << ")]"; + if (ap.remainder (ap.vpt) == ap.remainder (ap.ept1)) os << "--HS--"; + return os; +} + + +void Antipodal::update (CHVertex *pt) +{ + CHVertex *rpt = pt->right (); + CHVertex *lpt = pt->left (); + + int rmp = remainder (pt); + int rmv = remainder (vpt); + int rme = remainder (ept1); + int zpt = pt->get (iy); // vertical AP : Z -> X -- horizontal AP : Z -> Y + int zav = vpt->get (iy); // coord of antipodal vertex + int zas = ept1->get (iy); // coord of antipodal edge start + int zae = ept2->get (iy); // coord of antipodal edge end + + CHVertex *pvertex; + if (remainder (rpt) == rmv) pvertex = rpt; + else if (remainder (lpt) == rmv) pvertex = lpt; + else pvertex = vpt; + + CHVertex *pedge; + if (remainder (rpt) == rme) pedge = rpt; + else if (remainder (lpt) == rme) pedge = lpt; + else pedge = ept1; + + + // P on the line supported by the Edge + if (rmp == rme) + { + // P between start end end of antipodal Edge : no change (IMPOSSIBLE) + if ((zpt == zas) || (zpt == zae) || ((zpt < zas) != (zpt < zae))) return; + // -> prolongation of antipodal Edge up to P + setEdge (pt, pedge); + return; + } + + // P on the line (parallel to Edge) supported by the Vertex + if (rmp == rmv) + { + // P at the height of Edge -> P is the new Vertex + if ((zpt == zas) || (zpt == zae) || ((zpt < zas) != (zpt < zae))) + setVertex (pt); + else + { + // P beyond Edge Start : -> the Edge Start is the new Vertex + if ((zas == zae) || ((zas < zpt) != (zas < zae))) setVertex (ept1); + // P beyond Edge End : -> the Edge End is the new Vertex + if ((zae < zpt) != (zae < zas)) setVertex (ept2); + // -> the new Edge joins P to the former Vertex + setEdge (pt, pvertex); + } + return; + } + + // P strictly between antipodal Edge and Vertex -> no change + if ((rmp < rmv) != (rmp < rme)) return; + + + // P at the height of the antipodal Vertex + if (zpt == zav) + { + // P beyond the antipodal Vertex + if ((rmv < rmp) != (rmv < rme)) + { + // -> P is the new Vertex + setVertex (pt); + return; + } + + CHVertex *oldvpt = vpt; + if (zav != lpt->get (iy)) + { + if (oldvpt->vprod (oldvpt->left (), lpt, pt) > 0) + { + setVertex (oldvpt); + setEdge (lpt, pt); + } + else + { + setVertex (pt); + setEdge (oldvpt, oldvpt->left ()); + } + } + else + { + if (oldvpt->vprod (oldvpt->right (), rpt, pt) < 0) + { + setVertex (oldvpt); + setEdge (rpt, pt); + } + else + { + setVertex (pt); + setEdge (oldvpt, oldvpt->right ()); + } + } + return; + } + + + // Main case + //============================================================== + CHVertex *cvx = NULL; // candidate rotation vertex + CHVertex *lvx, *rvx; // left and right vertices of candidate + int zvx; // coord of candidate + + bool firstQuad = true; + if (edgeInFirstQuadrant ()) + { + if (((rmp > rme) && (rmp > rmv) && (zpt > zav)) + || ((rmp < rme) && (rmp < rmv) && (zpt < zav))) firstQuad = false; + } + else + if (((rmp > rme) && (rmp > rmv) && (zpt < zav)) + || ((rmp < rme) && (rmp < rmv) && (zpt > zav))) firstQuad = false; + + if (firstQuad) + { + if ((rme < rmp) != (rme < rmv)) cvx = pvertex; + if ((rmv < rme) != (rmv < rmp)) + cvx = (ept1->right () == ept2 ? ept1 : ept2); + zvx = cvx->get (iy); + lvx = cvx->left (); + rvx = cvx->right (); + + while (cvx->vprod (rvx, rpt, pt) > 0) + { + cvx = rvx; + lvx = cvx->left (); + rvx = cvx->right (); + zvx = cvx->get (iy); + int zpn = lvx->get (iy); + if ((zpt == zvx) || (zpt == zpn) || ((zpt < zvx) != (zpt < zpn))) break; + } + + if (zvx == zpt) + { + if (cvx->vprod (rvx, rpt, pt) <= 0) // Au lieu de < chez Phuong + { + setVertex (cvx); + setEdge (rpt, pt); + } + else + { + setVertex (pt); + setEdge (cvx, rvx); + } + } + else + { + int zpn = rpt->get (iy); + if ((zvx == zpn) || ((zvx < zpt) != (zvx < zpn))) + { + setVertex (cvx); + setEdge (rpt, pt); + } + else + { + setVertex (pt); + setEdge (lvx, cvx); + } + } + } + + else // second quadrant + { + if ((rme < rmp) != (rme < rmv)) cvx = pvertex; + if ((rmv < rme) != (rmv < rmp)) + cvx = (ept1->left () == ept2 ? ept1 : ept2); + zvx = cvx->get (iy); + rvx = cvx->right (); + lvx = cvx->left (); + + while (cvx->vprod (lvx, lpt, pt) < 0) + { + cvx = lvx; + rvx = cvx->right (); + lvx = cvx->left (); + zvx = cvx->get (iy); + int zvn = rvx->get (iy); + if ((zpt == zvx) || (zpt == zvn) || ((zpt < zvx) != (zpt < zvn))) break; + } + if (zvx == zpt) + { + if (cvx->vprod (lvx, lpt, pt) >= 0) + { + setVertex (cvx); + setEdge (lpt, pt); + } + else + { + setVertex (pt); + setEdge (cvx, lvx); + } + } + else + { + int zvn = lpt->get (iy); + if ((zvx == zvn) || ((zvx < zvn) != (zvx < zpt))) + { + setVertex (cvx); + setEdge (lpt, pt); + } + else + { + setVertex (pt); + setEdge (rvx, cvx); + } + } + } +} diff --git a/Code/Seg/ConvexHull/antipodal.h b/Code/Seg/ConvexHull/antipodal.h new file mode 100755 index 0000000000000000000000000000000000000000..4983403c66f75db1b3b7bbdf6a927bca585a5b48 --- /dev/null +++ b/Code/Seg/ConvexHull/antipodal.h @@ -0,0 +1,82 @@ +#ifndef ANTIPODAL_H +#define ANTIPODAL_H + +#include "chvertex.h" +#include <ostream> + +using namespace std; + + +class Antipodal +{ +public: + + /** Builds an empty (undetermined) horizontal antipodal pair. */ + Antipodal (); + + /** Sets the coordinates for a vertical antipodal pair. */ + inline void setVertical () { ix = 1; iy = 0; } + + /** Initializes the vertex/edge pair from three unordered vertices. */ + void init (CHVertex *vpt, CHVertex *ept1, CHVertex *ept2); + + /** Sets the vertex and the edge of the antipodal pair. */ + inline void setVertexAndEdge (CHVertex *pt, CHVertex *es, CHVertex *ee) { + vpt = pt; ept1 = es; ept2 = ee; } + + /** Sets the vertex of the antipodal pair. */ + inline void setVertex (CHVertex *pt) { vpt = pt; } + + /** Sets the edge of the antipodal pair. */ + inline void setEdge (CHVertex *pt1, CHVertex *pt2) { ept1 = pt1; ept2 = pt2; } + + /** Returns the vertex of the antipodal pair. */ + inline CHVertex *vertex () const { return vpt; } + + /** Returns the leaning edge start of the antipodal pair. */ + inline CHVertex *edgeStart () const { return ept1; } + + /** Returns the leaning edge end of the antipodal pair. */ + inline CHVertex *edgeEnd () const { return ept2; } + + /** Returns the horizontal or vertical width of the antipodal pair. */ + AbsRat rationalWidth () const; + + /** Gets the rational main axis width of the antipodal pair. */ + void width (int &num, int &den) const; + + /** Returns the remainder of the edge line equation for given vertex. */ + int remainder (CHVertex *v) const; + + /** Checks if the edge lies in the first quadrant (x = y). */ + bool edgeInFirstQuadrant () const; + + /** Gets the edge vector Y coordinate. */ + int getA () const; + + /** Gets the edge vector X coordinate. */ + int getB () const; + + /** Updates the antipodal pair after the insertion of a new vertex. */ + void update (CHVertex *pt); + + /** Returns a string that represents the antipodal pair. */ + friend ostream& operator<< (ostream &os, const Antipodal &ap); + + +protected: + + /** First coordinate (X for horizonal pair, Y for vertical pair). */ + int ix; + /** Second coordinate (Y for horizonal pair, X for vertical pair). */ + int iy; + + /** Leaning vertex. */ + CHVertex *vpt; + /** Start of the leaning edge. */ + CHVertex *ept1; + /** End of the leaning edge. */ + CHVertex *ept2; + +}; +#endif diff --git a/Code/Seg/ConvexHull/chvertex.cpp b/Code/Seg/ConvexHull/chvertex.cpp new file mode 100755 index 0000000000000000000000000000000000000000..95f857d5e5a75a62fab6137cb552d54e24a111fb --- /dev/null +++ b/Code/Seg/ConvexHull/chvertex.cpp @@ -0,0 +1,34 @@ +#include "chvertex.h" + + +CHVertex::CHVertex () : Pt2i () +{ + lv = NULL; + rv = NULL; +} + + +CHVertex::CHVertex (int x, int y) : Pt2i (x, y) +{ + lv = NULL; + rv = NULL; +} + + +CHVertex::CHVertex (const Pt2i &p) : Pt2i (p) +{ + lv = NULL; + rv = NULL; +} + + +CHVertex::~CHVertex () +{ +} + + +ostream& operator<< (ostream &os, const CHVertex &v) +{ + os << "(" << v.xp << ", " << v.yp << ")"; + return os; +} diff --git a/Code/Seg/ConvexHull/chvertex.h b/Code/Seg/ConvexHull/chvertex.h new file mode 100755 index 0000000000000000000000000000000000000000..543e35d0d12e7c4cfaa172e623725f458b7654e8 --- /dev/null +++ b/Code/Seg/ConvexHull/chvertex.h @@ -0,0 +1,51 @@ +#ifndef CHVERTEX_H +#define CHVERTEX_H + +#include "pt2i.h" +#include <iostream> + + +/** Chained point with two adjacent points. */ +class CHVertex : public Pt2i +{ + +public: + + /** Builds a default vertex. */ + CHVertex (); + + /** Builds a vertex on given coordinates. */ + CHVertex (int x, int y); + + /** Builds a vertex at the position of the given point. */ + CHVertex (const Pt2i &p); + ~CHVertex (); + + inline CHVertex *left () const { return lv; } + inline CHVertex *right () const { return rv; } + inline void setLeft (CHVertex *v) { lv = v; } + inline void setRight (CHVertex *v) { rv = v; } + + /** Returns the vector product of vector (this, pt) and vector (vx, vy) + */ + inline int vprod (CHVertex *pt, int vx, int vy) const { + return ((pt->xp - xp) * vy - vx * (pt->yp - yp)); } + + /** Returns the vector product of vector (this, p2) and vector (p3, p4) + */ + inline int vprod (CHVertex *p2, CHVertex *p3, CHVertex *p4) const { + return ((p2->xp - xp) * (p4->yp - p3->yp) + - (p4->xp - p3->xp) * (p2->yp - yp)); } + + friend ostream& operator<< (ostream &os, const CHVertex &v); + + +protected: + + /** Adjacent vertex to the left. */ + CHVertex *lv; + /** Adjacent vertex to the right. */ + CHVertex *rv; +}; + +#endif diff --git a/Code/Seg/ConvexHull/convexhull.cpp b/Code/Seg/ConvexHull/convexhull.cpp new file mode 100755 index 0000000000000000000000000000000000000000..d93a2575fc4cf81575d923e4b1c5cae3ff6b93ba --- /dev/null +++ b/Code/Seg/ConvexHull/convexhull.cpp @@ -0,0 +1,260 @@ +#include "convexhull.h" + + +ConvexHull::ConvexHull (const Pt2i &lpt, const Pt2i &cpt, const Pt2i &rpt) +{ + CHVertex *cvert = new CHVertex (cpt); + leftVertex = new CHVertex (lpt); + rightVertex = new CHVertex (rpt); + lastToLeft = false; + + if (lpt.toLeft (cpt, rpt)) + { + leftVertex->setRight (cvert); + cvert->setLeft (leftVertex); + cvert->setRight (rightVertex); + rightVertex->setLeft (cvert); + rightVertex->setRight (leftVertex); + leftVertex->setLeft (rightVertex); + } + else + { + leftVertex->setRight (rightVertex); + rightVertex->setLeft (leftVertex); + rightVertex->setRight (cvert); + cvert->setLeft (rightVertex); + cvert->setRight (leftVertex); + leftVertex->setLeft (cvert); + } + + aph.init (leftVertex, cvert, rightVertex); + apv.setVertical (); + apv.init (leftVertex, cvert, rightVertex); + + gbg.push_back (leftVertex); + gbg.push_back (cvert); + gbg.push_back (rightVertex); + + old_left = leftVertex; + old_right = rightVertex; + old_aph_vertex = aph.vertex (); + old_aph_edge_start = aph.edgeStart (); + old_aph_edge_end = aph.edgeEnd (); + old_apv_vertex = apv.vertex (); + old_apv_edge_start = apv.edgeStart (); + old_apv_edge_end = apv.edgeEnd (); +} + + +ConvexHull::~ConvexHull () +{ + for (int i = 0; i < (int) (gbg.size ()); ++i) delete gbg [i]; +} + + +void ConvexHull::preserve () +{ + old_aph_vertex = aph.vertex (); + old_aph_edge_start = aph.edgeStart (); + old_aph_edge_end = aph.edgeEnd (); + old_apv_vertex = apv.vertex (); + old_apv_edge_start = apv.edgeStart (); + old_apv_edge_end = apv.edgeEnd (); + old_left = leftVertex; + old_right = rightVertex; +} + + +void ConvexHull::restore () +{ + rconnect->setLeft (rdisconnect); + lconnect->setRight (ldisconnect); + leftVertex = old_left; + rightVertex = old_right; + aph.setVertexAndEdge (old_aph_vertex, old_aph_edge_start, old_aph_edge_end); + apv.setVertexAndEdge (old_apv_vertex, old_apv_edge_start, old_apv_edge_end); +} + + +bool ConvexHull::addPoint (const Pt2i &pix, bool toleft) +{ + if (inHull (pix, toleft)) return false; + CHVertex *pt = new CHVertex (pix); + lastToLeft = toleft; + gbg.push_back (pt); + preserve (); + insert (pt, toleft); + aph.update (pt); + apv.update (pt); + return true; +} + + +bool ConvexHull::addPointDS (const Pt2i &pix, bool toleft) +{ + CHVertex *pt = new CHVertex (pix); + lastToLeft = toleft; + gbg.push_back (pt); + preserve (); + insertDS (pt, toleft); + aph.update (pt); + apv.update (pt); + return true; +} + + +bool ConvexHull::moveLastPoint (const Pt2i &pix) +{ + restore (); + if (inHull (pix, lastToLeft)) return false; + gbg.pop_back (); + preserve (); + addPoint (pix, lastToLeft); + return true; +} + + +AbsRat ConvexHull::rationalThickness () const +{ + AbsRat aphw = aph.rationalWidth (); + AbsRat apvw = apv.rationalWidth (); + return (apvw.lessThan (aphw) ? apvw : aphw); +} + + +void ConvexHull::antipodalEdgeAndVertex (Pt2i &s, Pt2i &e, Pt2i &v) const +{ + int n1, d1, n2, d2; + aph.width (n1, d1); + apv.width (n2, d2); + const Antipodal *ap = ((n2 * d1 < n1 * d2) ? &apv : &aph); + s.set (*(ap->edgeStart ())); + e.set (*(ap->edgeEnd ())); + v.set (*(ap->vertex ())); +} + + +bool ConvexHull::inHull (const Pt2i &pix, bool toleft) const +{ + CHVertex *ext = (toleft ? leftVertex : rightVertex); + return (pix.toLeftOrOn (*ext, *(ext->right ())) + && pix.toLeftOrOn (*(ext->left ()), *ext)); +} + + +void ConvexHull::insert (CHVertex *pt, bool toleft) +{ + bool opIn = false; // Opposite polyline top in the new convex hull + CHVertex *opVertex = NULL; // Opposite vertex + + if (toleft) + { + lconnect = leftVertex; + rconnect = leftVertex; + leftVertex = pt; + opVertex = rightVertex; + } + else + { + lconnect = rightVertex; + rconnect = rightVertex; + rightVertex = pt; + opVertex = leftVertex; + } + + ldisconnect = lconnect->right (); + while (pt->toLeftOrOn (*lconnect, *(lconnect->left ()))) + { + if (lconnect == opVertex) opIn = true; + ldisconnect = lconnect; + lconnect = lconnect->left (); + } + if (opIn) + { + if (toleft) rightVertex = lconnect; + else leftVertex = lconnect; + } + + opIn = false; + rdisconnect = rconnect->left (); + while (! pt->toLeft (*rconnect, *(rconnect->right ()))) + { + if (rconnect == opVertex) opIn = true; + rdisconnect = rconnect; + rconnect = rconnect->right (); + } + if (opIn) + { + if (toleft) rightVertex = rconnect; + else leftVertex = rconnect; + } + + lconnect->setRight (pt); + pt->setLeft (lconnect); + rconnect->setLeft (pt); + pt->setRight (rconnect); +} + + +void ConvexHull::insertDS (CHVertex *pt, bool toleft) +{ + if (toleft) + { + lconnect = leftVertex; + rconnect = leftVertex; + leftVertex = pt; + } + else + { + lconnect = rightVertex; + rconnect = rightVertex; + rightVertex = pt; + } + + ldisconnect = lconnect->right (); + while (pt->toLeftOrOn (*lconnect, *(lconnect->left ()))) + { + ldisconnect = lconnect; + lconnect = lconnect->left (); + } + + rdisconnect = rconnect->left (); + while (! pt->toLeft (*rconnect, *(rconnect->right ()))) + { + rdisconnect = rconnect; + rconnect = rconnect->right (); + } + + lconnect->setRight (pt); + pt->setLeft (lconnect); + rconnect->setLeft (pt); + pt->setRight (rconnect); +} + + +ostream& operator<< (ostream &os, const ConvexHull &ch) +{ + os << "APH = " << ch.aph << endl; + os << "APV = " << ch.apv << endl; + os << "FIRST " << *(ch.leftVertex); + CHVertex *next = ch.leftVertex->right (); + int i = 0; + while (i++ < 20 && next != ch.leftVertex) + { + os << " - " << *next; + next = next->right (); + } + if (i >= 20) os << " ---"; + os << endl; + os << "LAST " << *(ch.rightVertex); + next = ch.rightVertex->left (); + i = 0; + while (i++ < 20 && next != ch.rightVertex) + { + os << " - " << *next; + next = next->left (); + } + if (i >= 20) os << " ---"; + + return os; +} diff --git a/Code/Seg/ConvexHull/convexhull.h b/Code/Seg/ConvexHull/convexhull.h new file mode 100755 index 0000000000000000000000000000000000000000..7d54e8688ff72433782c68aa5e7dbffdcd08cd6a --- /dev/null +++ b/Code/Seg/ConvexHull/convexhull.h @@ -0,0 +1,169 @@ +#ifndef CONVEXHULL +#define CONVEXHULL + + +#include <vector> +#include "antipodal.h" + +using namespace std; + + +class ConvexHull +{ + +public: + + /** + * \brief Creates a convex hull from a triangle. + * Be very careful with the points ordering : lpt, cpt, then rpt. + * Ensure that the points are NOT COLINEAR (not tested here). + * @param lpt : left end of the polyline. + * @param cpt : center of the polyline. + * @param rpt : right end of the polyline. + */ + ConvexHull (const Pt2i &lpt, const Pt2i &cpt, const Pt2i &rpt); + + /** + * \brief Deletes the convex hull. + * Removes all the registred vertices. + */ + ~ConvexHull (); + + /** + * \brief Restores the convexhull features after a modification. + */ + void restore (); + + + /** + * \brief Appends a new point at one side of the convex hull. + */ + bool addPoint (const Pt2i &pix, bool toleft); + + /** + * \brief Appends a new point at one side of the convex hull. + * To be used with directional scans : + * In that case, added point can not be inside the hull. + */ + bool addPointDS (const Pt2i &pix, bool toleft); + + /** + * \brief Moves the last entered point and returns the success. + */ + bool moveLastPoint (const Pt2i &pix); + + /** + * \brief Checks whether the line to the given point crosses the hull. + */ + bool inHull (const Pt2i &pix, bool toleft) const; + + + /** + * Returns the antipodal vertex. + * @param s Antipodal edge start. + * @param e Antipodal edge end. + * @param v Antipodal vertex. + */ + void antipodalEdgeAndVertex (Pt2i &s, Pt2i &e, Pt2i &v) const; + + /** + * Returns the minimal vertical or horizontal thickness of the convex hull. + */ + AbsRat rationalThickness () const; + + /** + * Returns a string that represents the convex hull. + */ + friend ostream& operator<< (ostream &os, const ConvexHull &ch); + + /** + * Returns the first vertex of the convex hull. + */ + inline CHVertex *getFirstVertex () const { return (leftVertex); } + + /** + * Returns the last vertex of the convex hull. + */ + inline CHVertex *getLastVertex () const { return (rightVertex); } + + /** + * Returns the horizontal antipodal vertex. + */ + inline CHVertex *getAphVertex () const { return (aph.vertex ()); } + + /** + * Returns the horizontal antipodal edge start. + */ + inline CHVertex *getAphEdgeStart () const { return (aph.edgeStart ()); } + + /** + * Returns the horizontal antipodal edge end. + */ + inline CHVertex *getAphEdgeEnd () const { return (aph.edgeEnd ()); } + + /** + * Returns the vertical antipodal vertex. + */ + inline CHVertex *getApvVertex () const { return (apv.vertex ()); } + + /** + * Returns the vertical antipodal edge start. + */ + inline CHVertex *getApvEdgeStart () const { return (apv.edgeStart ()); } + + /** + * Returns the vertical antipodal edge end. + */ + inline CHVertex *getApvEdgeEnd () const { return (apv.edgeEnd ()); } + + +private: + + /** + * Stores the convexhull features before a modification. + */ + void preserve (); + + /** + * Inserts a new point into the convex hull. + */ + void insert (CHVertex *pt, bool toleft); + + /** + * Inserts a new point into the convex hull. + * To be used with directional scans : + * In that case, opposite ends of the polyline can never pass each other. + */ + void insertDS (CHVertex *pt, bool toleft); + + +protected: + + /** Polyline left end point. */ + CHVertex *leftVertex; + /** Polyline right end point. */ + CHVertex *rightVertex; + /** Indicates if the last vertex was entered to the left. */ + bool lastToLeft; + + /** Antipodal pair in horizontal direction. */ + Antipodal aph; + /** Antipodal pair in vertical direction. */ + Antipodal apv; + + /** Registration of previous horizontal antipodal pair. */ + CHVertex *old_aph_vertex, *old_aph_edge_start, *old_aph_edge_end;; + /** Registration of previous vertical antipodal pair. */ + CHVertex *old_apv_vertex, *old_apv_edge_start, *old_apv_edge_end; + /** Registration of previous polyline left end point. */ + CHVertex *old_left; + /** Registration of previous polyline right end point. */ + CHVertex *old_right; + /** Registration of previous connections. */ + CHVertex *lconnect, *ldisconnect, *rconnect, *rdisconnect; + + /** Collection of vertices for clearance. */ + vector<CHVertex*> gbg; + +}; +#endif diff --git a/Code/Seg/DirectionalScanner/directionalscanner.cpp b/Code/Seg/DirectionalScanner/directionalscanner.cpp new file mode 100755 index 0000000000000000000000000000000000000000..2c94dbd2632a39b5bf07d469318d4163bec4057f --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscanner.cpp @@ -0,0 +1,18 @@ +#include <cstdlib> +#include "directionalscanner.h" + + + +void DirectionalScanner::bindTo (int a, int b, int c) +{ + (void) a; + (void) b; + (void) c; +} + + +DirectionalScanner::~DirectionalScanner () +{ + if (steps != NULL) delete steps; + steps = NULL; +} diff --git a/Code/Seg/DirectionalScanner/directionalscanner.h b/Code/Seg/DirectionalScanner/directionalscanner.h new file mode 100755 index 0000000000000000000000000000000000000000..d7a6870ff749ea2527c522a2336ee6543d7315fb --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscanner.h @@ -0,0 +1,110 @@ +#ifndef DIRECTIONAL_SCANNER_H +#define DIRECTIONAL_SCANNER_H + +#include <cstdlib> +#include <vector> +#include "pt2i.h" + +using namespace std; + + + +/** + * @class DirectionalScanner directionalscanner.h + * \brief Incremental directional scanner. + * This scanner iterately provides parallel scan lines. + * \author {P. Even and B. Kerautret} + */ +class DirectionalScanner +{ + +public: + + /** + * @fn ~DirectionalScan() + * \brief Deletes the scan strip. + */ + virtual ~DirectionalScanner (); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + virtual int first (vector<Pt2i> &scan) = 0; + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + virtual int nextOnLeft (vector<Pt2i> &scan) = 0; + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + virtual int nextOnRight (vector<Pt2i> &scan) = 0; + + /** + * @fn bindTo(int a, int b, int c) + * \brief Binds the scan stripe to wrap the given digital line. + * Resets the bounding lines parameters to center on the given line. + * @param a New value for the 'a' parameter of the current scan stripe. + * @param b New value for the 'b' parameter of the current scan stripe. + * @param c New value for the medial axis of the current scan stripe. + */ + virtual void bindTo (int a, int b, int c); + + +protected: + + /** Scanable area. */ + int xmin, ymin, xmax, ymax; + + /** Coefficients of the discrete upper support line. */ + int dla, dlb, dlc2; + + /** Size of the discrete line pattern. */ + int nbs; + + /** Discrete line pattern and its end. */ + bool *steps, *fs; + + /** Start position of a scan for both directions. */ + int lcx, lcy, rcx, rcy; + + /** Current step in scan direction. */ + bool *lst2, *rst2; + + DirectionalScanner () { } + + /** + * @fn DirectionalScanner(int xmini, int ymini, int xmaxi, int ymaxi, + * int nb, bool* st, int sx, int sy) + * \brief Creates an incremental directional scanner. + * Creates a directional scanner from pattern and start. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point and a line pattern. + * @param xmini left border of the scan area + * @param ymini low border of the scan area + * @param xmaxi right border of the scan area + * @param ymaxi up border of the scan area + * @param nb size of the support line pattern + * @param st support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DirectionalScanner (int xmini, int ymini, int xmaxi, int ymaxi, + int nb, bool* st, int sx, int sy) + : xmin (xmini), ymin (ymini), xmax (xmaxi), ymax (ymaxi), + nbs (nb), steps (st), + lcx (sx), lcy (sy), rcx (sx), rcy (sy) { } + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/directionalscannero1.cpp b/Code/Seg/DirectionalScanner/directionalscannero1.cpp new file mode 100755 index 0000000000000000000000000000000000000000..a31f3109df263334adc589e52781c357f701f578 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero1.cpp @@ -0,0 +1,223 @@ +#include <cstdlib> +#include <iostream> +#include "directionalscannero1.h" + + + +DirectionalScannerO1::DirectionalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO1::DirectionalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc2 = c1; + c1 = c2; + } + else this->dlc2 = c2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx ++; + lcy --; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO1::DirectionalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx ++; + lcy --; + } + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cx--; + cy++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + ltransition = false; + rtransition = false; +} + + +int DirectionalScannerO1::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO1::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (ltransition) + { + lcy --; + if (--lst2 < steps) lst2 = fs - 1; + ltransition = false; + } + else + { + if (--lst1 < steps) lst1 = fs - 1; + lcx --; + if (*lst1) + { + lcy --; + if (--lst2 < steps) lst2 = fs - 1; + if (*lst2) + { + if (++lst2 >= fs) lst2 = steps; + lcy ++; + ltransition = true; + } + } + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO1::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (rtransition) + { + rcx ++; + rtransition = false; + } + else + { + rcx ++; + if (*rst1) + { + if (*rst2) + { + rcx --; + rtransition = true; + } + rcy ++; + if (++rst2 >= fs) rst2 = steps; + } + if (++rst1 >= fs) rst1 = steps; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/directionalscannero1.h b/Code/Seg/DirectionalScanner/directionalscannero1.h new file mode 100755 index 0000000000000000000000000000000000000000..ee48df78dc0af2356f7ee832503110ee89119597 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero1.h @@ -0,0 +1,125 @@ +#ifndef DIRECTIONAL_SCANNER_O1_H +#define DIRECTIONAL_SCANNER_O1_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DirectionalScannerO1 directionalscannero1.h + * \brief Incremental directional scanner for the 1st octant. + * \author {P. Even and B. Kerautret} + */ +class DirectionalScannerO1 : public DirectionalScanner +{ + +public: + + /** + * @fn DirectionalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DirectionalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DirectionalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DirectionalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DirectionalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DirectionalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +private: + + /** Current step in strip direction */ + bool *lst1, *rst1; + + /** State of the scan */ + bool ltransition, rtransition; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/directionalscannero2.cpp b/Code/Seg/DirectionalScanner/directionalscannero2.cpp new file mode 100755 index 0000000000000000000000000000000000000000..5d6ff02db5149207e16fcee6fa46ded209aa663d --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero2.cpp @@ -0,0 +1,222 @@ +#include <cstdlib> +#include <iostream> +#include "directionalscannero2.h" + + + +DirectionalScannerO2::DirectionalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO2::DirectionalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc2 = c1; + c1 = c2; + } + else this->dlc2 = c2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx ++; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO2::DirectionalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx ++; + } + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cy++; + cx--; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + ltransition = false; + rtransition = false; +} + + +int DirectionalScannerO2::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO2::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (ltransition) + { + lcy --; + ltransition = false; + } + else + { + if (--lst1 < steps) lst1 = fs - 1; + lcy --; + if (*lst1) + { + lcx --; + if (*lst2) + { + lcy ++; + ltransition = true; + } + if (++lst2 >= fs) lst2 = steps; + } + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO2::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (rtransition) + { + rcx ++; + if (--rst2 < steps) rst2 = fs - 1; + rtransition = false; + } + else + { + rcy ++; + if (*rst1) + { + if (--rst2 < steps) rst2 = fs - 1; + if (*rst2) + { + if (++rst2 >= fs) rst2 = steps; + rtransition = true; + } + else rcx ++; + } + if (++rst1 >= fs) rst1 = steps; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/directionalscannero2.h b/Code/Seg/DirectionalScanner/directionalscannero2.h new file mode 100755 index 0000000000000000000000000000000000000000..6e06b5dfc3715467a494b49e27291b71a56e64e8 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero2.h @@ -0,0 +1,125 @@ +#ifndef DIRECTIONAL_SCANNER_O2_H +#define DIRECTIONAL_SCANNER_O2_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DirectionalScannerO2 directionalscannero2.h + * \brief Incremental directional scanner for the 2nd octant. + * \author {P. Even and B. Kerautret} + */ +class DirectionalScannerO2 : public DirectionalScanner +{ + +public: + + /** + * @fn DirectionalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DirectionalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DirectionalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DirectionalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DirectionalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DirectionalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +private: + + /** Current step in strip direction */ + bool *lst1, *rst1; + + /** State of the scan */ + bool ltransition, rtransition; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/directionalscannero7.cpp b/Code/Seg/DirectionalScanner/directionalscannero7.cpp new file mode 100755 index 0000000000000000000000000000000000000000..c62fb5313c81f0f4b80eac54c530c84d5a6e31e1 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero7.cpp @@ -0,0 +1,223 @@ +#include <cstdlib> +#include <iostream> +#include "directionalscannero7.h" + + + +DirectionalScannerO7::DirectionalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO7::DirectionalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc2 = c1; + c1 = c2; + } + else this->dlc2 = c2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO7::DirectionalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx --; + } + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cy++; + cx++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + ltransition = false; + rtransition = false; +} + + + +int DirectionalScannerO7::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO7::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (ltransition) + { + lcx --; + if (--lst2 < steps) lst2 = fs - 1; + ltransition = false; + } + else + { + if (--lst1 < steps) lst1 = fs - 1; + lcy ++; + if (*lst1) + { + if (--lst2 < steps) lst2 = fs - 1; + if (*lst2) + { + if (++lst2 >= fs) lst2 = steps; + ltransition = true; + } + else lcx --; + } + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO7::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (rtransition) + { + rcy --; + rtransition = false; + } + else + { + rcy --; + if (*rst1) + { + rcx ++; + if (*rst2) + { + rcy ++; + rtransition = true; + } + if (++rst2 >= fs) rst2 = steps; + } + if (++rst1 >= fs) rst1 = steps; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst == fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst == fs) nst = steps; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/directionalscannero7.h b/Code/Seg/DirectionalScanner/directionalscannero7.h new file mode 100755 index 0000000000000000000000000000000000000000..404338da972dbf320783d38c1e352e5e4d08a63f --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero7.h @@ -0,0 +1,125 @@ +#ifndef DIRECTIONAL_SCANNER_O7_H +#define DIRECTIONAL_SCANNER_O7_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DirectionalScannerO7 directionalscannero7.h + * \brief Incremental directional scanner for the 7th octant. + * \author {P. Even and B. Kerautret} + */ +class DirectionalScannerO7 : public DirectionalScanner +{ + +public: + + /** + * @fn DirectionalScannerO7(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DirectionalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DirectionalScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DirectionalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DirectionalScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DirectionalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +private: + + /** Current step in strip direction */ + bool *lst1, *rst1; + + /** State of the scan */ + bool ltransition, rtransition; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/directionalscannero8.cpp b/Code/Seg/DirectionalScanner/directionalscannero8.cpp new file mode 100755 index 0000000000000000000000000000000000000000..3b78148bc9b92003e0efbcf19bbfbf2d60340112 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero8.cpp @@ -0,0 +1,223 @@ +#include <cstdlib> +#include <iostream> +#include "directionalscannero8.h" + + + +DirectionalScannerO8::DirectionalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO8::DirectionalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc2 = c1; + c1 = c2; + } + else this->dlc2 = c2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx --; + lcy --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; + ltransition = false; + rtransition = false; +} + + + +DirectionalScannerO8::DirectionalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx --; + lcy --; + } + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cx++; + cy++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + rcx = lcx; + rcy = lcy; + lst1 = steps; + rst1 = steps; + lst2 = steps; + rst2 = steps; + ltransition = false; + rtransition = false; +} + + + +int DirectionalScannerO8::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO8::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (ltransition) + { + lcx --; + ltransition = false; + } + else + { + if (--lst1 < steps) lst1 = fs - 1; + lcx --; + if (*lst1) + { + lcy ++; + if (*lst2) + { + lcx ++; + ltransition = true; + } + if (++lst2 >= fs) lst2 = steps; + } + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DirectionalScannerO8::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + if (rtransition) + { + rcy --; + if (--rst2 < steps) rst2 = fs - 1; + rtransition = false; + } + else + { + rcx ++; + if (*rst1) + { + if (--rst2 < steps) rst2 = fs - 1; + if (*rst2) + { + if (++rst2 >= fs) rst2 = steps; + rtransition = true; + } + else rcy --; + } + if (++rst1 >= fs) rst1 = steps; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/directionalscannero8.h b/Code/Seg/DirectionalScanner/directionalscannero8.h new file mode 100755 index 0000000000000000000000000000000000000000..0e8bea1c6cae99e0f1cfef3a25c8d0cfa443b7c2 --- /dev/null +++ b/Code/Seg/DirectionalScanner/directionalscannero8.h @@ -0,0 +1,125 @@ +#ifndef DIRECTIONAL_SCANNER_O8_H +#define DIRECTIONAL_SCANNER_O8_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DirectionalScannerO8 directionalscannero8.h + * \brief Incremental directional scanner for the 8th octant. + * \author {P. Even and B. Kerautret} + */ +class DirectionalScannerO8 : public DirectionalScanner +{ + +public: + + /** + * @fn DirectionalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DirectionalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DirectionalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DirectionalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DirectionalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DirectionalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +private: + + /** Current step in strip direction */ + bool *lst1, *rst1; + + /** State of the scan */ + bool ltransition, rtransition; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero1.cpp b/Code/Seg/DirectionalScanner/dynamicalscannero1.cpp new file mode 100755 index 0000000000000000000000000000000000000000..a3cad70c82ed2d953a3863d2394b720510a375f0 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero1.cpp @@ -0,0 +1,245 @@ +#include <cstdlib> +#include <iostream> +#include "dynamicalscannero1.h" + + + +DynamicalScannerO1::DynamicalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + this->dlc1 = a * sx + b * sy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO1::DynamicalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx ++; + lcy --; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO1::DynamicalScannerO1 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx ++; + lcy --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cx--; + cy++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + +int DynamicalScannerO1::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO1::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcx --; + // Whenever the control line changed + while (lcy < ymax - 1 && lcx >= xmin && dla * lcx + dlb * lcy > dlc1) + { + if (*lst2) lcx --; + lcy ++; + if (++lst2 >= fs) lst2 = steps; + } + while (lcy > ymin && lcx < xmax && dla * lcx + dlb * lcy < dlc1) + { + if (--lst2 < steps) lst2 = steps + nbs - 1; + if (*lst2) lcx ++; + lcy --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x --; + y ++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x --; + y ++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO1::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcx ++; + while (rcy < ymax - 1 && rcx >= xmin && dla * rcx + dlb * rcy > dlc1) + { + if (*rst2) rcx --; + rcy ++; + if (++rst2 >= fs) rst2 = steps; + } + while (rcy > ymin && rcx < xmax && dla * rcx + dlb * rcy < dlc1) + { + if (--rst2 < steps) rst2 = steps + nbs - 1; + if (*rst2) rcx ++; + rcy --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((x >= xmax || y < ymin) && dla * x + dlb * y >= dlc2) + { + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x--; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +void DynamicalScannerO1::bindTo (int a, int b, int c) +{ + if (a < 0) + { + dla = -a; + dlb = -b; + c = -c; + } + else + { + dla = a; + dlb = b; + } + int old_b = (templ_b < 0 ? -templ_b : templ_b); + int old_n1 = templ_a + old_b; + int old_ninf = (old_b > templ_a ? old_b : templ_a); + int new_a = (a < 0 ? -a : a); + int new_b = (b < 0 ? -b : b); + int new_n1 = new_a + new_b; + int new_ninf = (new_b > new_a ? new_b : new_a); + int nu; + if (new_n1 * old_ninf > old_n1 * new_ninf) + nu = (templ_nu * new_n1) / old_n1; + else + nu = (templ_nu * new_ninf) / old_ninf; + dlc1 = c + nu / 2; + dlc2 = c - nu / 2; +} diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero1.h b/Code/Seg/DirectionalScanner/dynamicalscannero1.h new file mode 100755 index 0000000000000000000000000000000000000000..4756ef31f2382fc76a8b4d67fc8e6bf48947c62c --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero1.h @@ -0,0 +1,137 @@ +#ifndef DYNAMICAL_SCANNER_O1_H +#define DYNAMICAL_SCANNER_O1_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DynamicalScannerO1 dynamicalscannero1.h + * \brief Dynamical directional scanner for the 1st octant. + * \author {P. Even} + */ +class DynamicalScannerO1 : public DirectionalScanner +{ + +public: + + /** + * @fn DynamicalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DynamicalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DynamicalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DynamicalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DynamicalScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length . + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DynamicalScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + + /** + * @fn bindTo(int a, int b, int c) + * \brief Binds the scan stripe to wrap the given digital line. + * Resets the bounding lines parameters to center on the given line. + * @param a New value for the 'a' parameter of the current scan stripe. + * @param b New value for the 'b' parameter of the current scan stripe. + * @param c New value for the medial axis of the current scan stripe. + */ + void bindTo (int a, int b, int c); + +protected : + + DynamicalScannerO1 () { } + + /** Coefficients of the template discrete support lines */ + int templ_a, templ_b, templ_nu; + + /** Intercept coefficient of the discrete lower support line */ + int dlc1; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero2.cpp b/Code/Seg/DirectionalScanner/dynamicalscannero2.cpp new file mode 100755 index 0000000000000000000000000000000000000000..bfb7a6a6d6ad3a81e8cf95522298e3cad9c62294 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero2.cpp @@ -0,0 +1,245 @@ +#include <cstdlib> +#include <iostream> +#include "dynamicalscannero2.h" + + + +DynamicalScannerO2::DynamicalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + this->dlc1 = a * sx + b * sy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO2::DynamicalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx ++; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO2::DynamicalScannerO2 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx ++; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cy++; + cx--; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + +int DynamicalScannerO2::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO2::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcy --; + // Whenever the control line changed + while (lcx > xmin && lcy < ymax && dla * lcx + dlb * lcy > dlc1) + { + if (*lst2) lcy ++; + lcx --; + if (++lst2 >= fs) lst2 = steps; + } + while (lcx < xmax - 1 && lcy >= ymin && dla * lcx + dlb * lcy < dlc1) + { + if (--lst2 < steps) lst2 = steps + nbs - 1; + if (*lst2) lcy --; + lcx ++; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO2::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcy ++; + while (rcx > xmin && rcy < ymax && dla * rcx + dlb * rcy > dlc1) + { + if (*rst2) rcy ++; + rcx --; + if (++rst2 >= fs) rst2 = steps; + } + while (rcx < xmax - 1 && rcy >= ymin && dla * rcx + dlb * rcy < dlc1) + { + if (--rst2 < steps) rst2 = steps + nbs - 1; + if (*rst2) rcy --; + rcx ++; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x--; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +void DynamicalScannerO2::bindTo (int a, int b, int c) +{ + if (a < 0) + { + dla = -a; + dlb = -b; + c = -c; + } + else + { + dla = a; + dlb = b; + } + int old_b = (templ_b < 0 ? -templ_b : templ_b); + int old_n1 = templ_a + old_b; + int old_ninf = (old_b > templ_a ? old_b : templ_a); + int new_a = (a < 0 ? -a : a); + int new_b = (b < 0 ? -b : b); + int new_n1 = new_a + new_b; + int new_ninf = (new_b > new_a ? new_b : new_a); + int nu; + if (new_n1 * old_ninf > old_n1 * new_ninf) + nu = (templ_nu * new_n1) / old_n1; + else + nu = (templ_nu * new_ninf) / old_ninf; + dlc1 = c + nu / 2; + dlc2 = c - nu / 2; +} diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero2.h b/Code/Seg/DirectionalScanner/dynamicalscannero2.h new file mode 100755 index 0000000000000000000000000000000000000000..e7500c3b89c0f5c07e8384347ae652704949c037 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero2.h @@ -0,0 +1,137 @@ +#ifndef DYNAMICAL_SCANNER_O2_H +#define DYNAMICAL_SCANNER_O2_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DynamicalScannerO2 dynamicalscannero2.h + * \brief Dynamical directional scanner for the 2nd octant. + * \author {P. Even and B. Kerautret} + */ +class DynamicalScannerO2 : public DirectionalScanner +{ + +public: + + /** + * @fn DynamicalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DynamicalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DynamicalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DynamicalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DynamicalScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DynamicalScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + + /** + * @fn bindTo(int a, int b, int c) + * \brief Binds the scan stripe to wrap the given digital line. + * Resets the bounding lines parameters to center on the given line. + * @param a New value for the 'a' parameter of the current scan stripe. + * @param b New value for the 'b' parameter of the current scan stripe. + * @param c New value for the medial axis of the current scan stripe. + */ + void bindTo (int a, int b, int c); + +protected : + + DynamicalScannerO2 () { } + + /** Coefficients of the template discrete support lines */ + int templ_a, templ_b, templ_nu; + + /** Intercept coefficient of the discrete lower support line */ + int dlc1; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero7.cpp b/Code/Seg/DirectionalScanner/dynamicalscannero7.cpp new file mode 100755 index 0000000000000000000000000000000000000000..5e05a2bbc932242886d074890d020a4cc1636504 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero7.cpp @@ -0,0 +1,246 @@ +#include <cstdlib> +#include <iostream> +#include "dynamicalscannero7.h" + + + +DynamicalScannerO7::DynamicalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + this->dlc1 = a * sx + b * sy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO7::DynamicalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO7::DynamicalScannerO7 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcy --; + lcx --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cy++; + cx++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + + +int DynamicalScannerO7::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO7::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcy ++; + while (lcx < xmax - 1 && lcy < ymax && dla * lcx + dlb * lcy < dlc1) + { + if (*lst2) lcy ++; + lcx ++; + if (++lst2 >= fs) lst2 = steps; + } + while (lcx > xmin && lcy >= ymin && dla * lcx + dlb * lcy > dlc1) + { + if (--lst2 < steps) lst2 = steps + nbs - 1; + if (*lst2) lcy --; + lcx --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO7::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcy --; + // Whenever the control corridor changed + while (rcx < xmax - 1 && rcy < ymax && dla * rcx + dlb * rcy < dlc1) + { + if (*rst2) rcy ++; + rcx ++; + if (++rst2 >= fs) rst2 = steps; + } + while (rcx > xmin && rcy >= ymin && dla * rcx + dlb * rcy > dlc1) + { + if (--rst2 < steps) rst2 = steps + nbs - 1; + if (*rst2) rcy --; + rcx --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((y < ymin || x < xmin) && dla * x + dlb * y <= dlc2) + { + if (*nst) y++; + x++; + if (++nst == fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && y < ymax && x < xmax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) y++; + x++; + if (++nst == fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +void DynamicalScannerO7::bindTo (int a, int b, int c) +{ + if (a < 0) + { + dla = -a; + dlb = -b; + c = -c; + } + else + { + dla = a; + dlb = b; + } + int old_b = (templ_b < 0 ? -templ_b : templ_b); + int old_n1 = templ_a + old_b; + int old_ninf = (old_b > templ_a ? old_b : templ_a); + int new_a = (a < 0 ? -a : a); + int new_b = (b < 0 ? -b : b); + int new_n1 = new_a + new_b; + int new_ninf = (new_b > new_a ? new_b : new_a); + int nu; + if (new_n1 * old_ninf > old_n1 * new_ninf) + nu = (templ_nu * new_n1) / old_n1; + else + nu = (templ_nu * new_ninf) / old_ninf; + dlc1 = c - nu / 2; + dlc2 = c + nu / 2; +} diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero7.h b/Code/Seg/DirectionalScanner/dynamicalscannero7.h new file mode 100755 index 0000000000000000000000000000000000000000..34174fcaa737f971efb73bdfbea123d821b69b50 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero7.h @@ -0,0 +1,137 @@ +#ifndef DYNAMICAL_SCANNER_O7_H +#define DYNAMICAL_SCANNER_O7_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DynamicalScannerO7 controlablescannero7.h + * \brief Dynamical directional scanner for the 7th octant. + * \author {P. Even and B. Kerautret} + */ +class DynamicalScannerO7 : public DirectionalScanner +{ + +public: + + /** + * @fn DynamicalScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DynamicalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DynamicalScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DynamicalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DynamicalScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DynamicalScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + + /** + * @fn bindTo(int a, int b, int c) + * \brief Binds the scan stripe to wrap the given digital line. + * Resets the bounding lines parameters to center on the given line. + * @param a New value for the 'a' parameter of the current scan stripe. + * @param b New value for the 'b' parameter of the current scan stripe. + * @param c New value for the medial axis of the current scan stripe. + */ + void bindTo (int a, int b, int c); + +protected : + + DynamicalScannerO7 () { } + + /** Coefficients of the template discrete support lines */ + int templ_a, templ_b, templ_nu; + + /** Intercept coefficient of the discrete lower support line */ + int dlc1; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero8.cpp b/Code/Seg/DirectionalScanner/dynamicalscannero8.cpp new file mode 100755 index 0000000000000000000000000000000000000000..ae42796b1c7427341166944879e130001fcc5a50 --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero8.cpp @@ -0,0 +1,246 @@ +#include <cstdlib> +#include <iostream> +#include "dynamicalscannero8.h" + + + +DynamicalScannerO8::DynamicalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, sx, sy) +{ + this->dla = a; + this->dlb = b; + this->dlc2 = c; + this->dlc1 = a * sx + b * sy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO8::DynamicalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + // Looking for the central scan start position + bool *st = steps + nbs; + do + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx --; + lcy --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +DynamicalScannerO8::DynamicalScannerO8 ( + int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, int cx, int cy, int length) + : DirectionalScanner (xmin, ymin, xmax, ymax, + nbs, steps, cx, cy) +{ + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + bool *st = steps + nbs; + for (int i = 0; i < w_2; i++) + { + if (--st < steps) st = steps + nbs - 1; + if (*st) lcx --; + lcy --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + st = steps; + while (w_2-- > 0) + { + if (*st) cx++; + cy++; + if (++st >= fs) st = steps; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + + +int DynamicalScannerO8::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + bool *nst = steps; // Current step in scan direction (jpts) + + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO8::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcx --; + while (lcy < ymax - 1 && lcx < xmax && dla * lcx + dlb * lcy < dlc1) + { + if (*lst2) lcx ++; + lcy ++; + if (++lst2 >= fs) lst2 = steps; + } + while (lcy > ymin && lcx >= xmin && dla * lcx + dlb * lcy > dlc1) + { + if (--lst2 < steps) lst2 = steps + nbs - 1; + if (*lst2) lcx --; + lcy --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + bool *nst = lst2; + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +int DynamicalScannerO8::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcx ++; + // Whenever the control corridor changed + while (rcy < ymax - 1 && rcx < xmax && dla * rcx + dlb * rcy < dlc1) + { + if (*rst2) rcx ++; + rcy ++; + if (++rst2 >= fs) rst2 = steps; + } + while (rcy > ymin && rcx >= xmin && dla * rcx + dlb * rcy > dlc1) + { + if (--rst2 < steps) rst2 = steps + nbs - 1; + if (*rst2) rcx --; + rcy --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + bool *nst = rst2; + while ((x < xmin || y < ymin) && dla * x + dlb * y <= dlc2) + { + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + while (dla * x + dlb * y <= dlc2 && x < xmax && y < ymax) + { + scan.push_back (Pt2i (x, y)); + if (*nst) x++; + y++; + if (++nst >= fs) nst = steps; + } + return ((int) (scan.size ())); +} + + +void DynamicalScannerO8::bindTo (int a, int b, int c) +{ + if (a < 0) + { + dla = -a; + dlb = -b; + c = -c; + } + else + { + dla = a; + dlb = b; + } + int old_b = (templ_b < 0 ? -templ_b : templ_b); + int old_n1 = templ_a + old_b; + int old_ninf = (old_b > templ_a ? old_b : templ_a); + int new_a = (a < 0 ? -a : a); + int new_b = (b < 0 ? -b : b); + int new_n1 = new_a + new_b; + int new_ninf = (new_b > new_a ? new_b : new_a); + int nu; + if (new_n1 * old_ninf > old_n1 * new_ninf) + nu = (templ_nu * new_n1) / old_n1; + else + nu = (templ_nu * new_ninf) / old_ninf; + dlc1 = c - nu / 2; + dlc2 = c + nu / 2; +} diff --git a/Code/Seg/DirectionalScanner/dynamicalscannero8.h b/Code/Seg/DirectionalScanner/dynamicalscannero8.h new file mode 100755 index 0000000000000000000000000000000000000000..5944231351d80134eb2e7cef35e1274465c0a43b --- /dev/null +++ b/Code/Seg/DirectionalScanner/dynamicalscannero8.h @@ -0,0 +1,137 @@ +#ifndef DYNAMICAL_SCANNER_O8_H +#define DYNAMICAL_SCANNER_O8_H + +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class DynamicalScannerO8 dynamicalscannero8.h + * \brief Dynamical directional scanner for the 8th octant. + * \author {P. Even and B. Kerautret} + */ +class DynamicalScannerO8 : public DirectionalScanner +{ + +public: + + /** + * @fn DynamicalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int mu2, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a directional scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + DynamicalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn DynamicalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a directional scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + DynamicalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn DynamicalScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, + * int nbs, bool *steps, int cx, int cy, int length) + * \brief Creates a directional scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + DynamicalScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, + int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + + /** + * @fn bindTo(int a, int b, int c) + * \brief Binds the scan stripe to wrap the given digital line. + * Resets the bounding lines parameters to center on the given line. + * @param a New value for the 'a' parameter of the current scan stripe. + * @param b New value for the 'b' parameter of the current scan stripe. + * @param c New value for the medial axis of the current scan stripe. + */ + void bindTo (int a, int b, int c); + +protected : + + DynamicalScannerO8 () { } + + /** Coefficients of the template discrete support lines */ + int templ_a, templ_b, templ_nu; + + /** Intercept coefficient of the discrete lower support line */ + int dlc1; + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/scannerprovider.cpp b/Code/Seg/DirectionalScanner/scannerprovider.cpp new file mode 100755 index 0000000000000000000000000000000000000000..6cc5bedf73e0ca954c373d0c7fa91967570431a7 --- /dev/null +++ b/Code/Seg/DirectionalScanner/scannerprovider.cpp @@ -0,0 +1,264 @@ +#include <cstdlib> +#include <iostream> +#include "scannerprovider.h" +#include "directionalscannero2.h" +#include "directionalscannero7.h" +#include "directionalscannero1.h" +#include "directionalscannero8.h" +#include "vhscannero2.h" +#include "vhscannero7.h" +#include "vhscannero1.h" +#include "vhscannero8.h" + + + +DirectionalScanner *ScannerProvider::getScanner ( + Pt2i p1, Pt2i p2, + int xmin, int ymin, int xmax, int ymax) +{ + // Enforces P1 to be lower than P2 + // or to left of P2 in cas of equality + if ((p1.y () > p2.y ()) + || ((p1.y () == p2.y ()) && (p1.x () > p2.x ()))) + { + Pt2i tmp = p1; + p1 = p2; + p2 = tmp; + } + + // Computes the steps position array + int nbs = 0; + bool *steps = p1.stepsTo (p2, &nbs); + + // Equation of the strip support lines : ax + by = c + int a = p2.x () - p1.x (); + int b = p2.y () - p1.y (); + if (a < 0 || (a == 0 && b < 0)) // Enforces a >= 0, then b > 0 + { + a = -a; + b = -b; + } + int c2 = a * p2.x () + b * p2.y (); + + // Builds and returns the appropriate scanner + if (b < 0) + if (-b > a) + { + if (isOrtho) + { + int repx = (p1.x () + p2.x ()) / 2; // central scan start + int repy = p1.y () - (int) ((p1.x () - repx) * (p1.x () - p2.x ()) + / (p2.y () - p1.y ())); + return (new VHScannerO1 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, repx, repy)); + } + else return (new DirectionalScannerO1 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, p1.x (), p1.y ())); + } + else + { + if (isOrtho) + { + int repy = (p1.y () + p2.y ()) / 2; // central scan start + int repx = p1.x () + (int) ((repy - p1.y ()) * (p2.y () - p1.y ()) + / (p1.x () - p2.x ())); + return (new VHScannerO2 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, repx, repy)); + } + else return (new DirectionalScannerO2 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, p1.x (), p1.y ())); + } + else + if (b > a) + { + if (isOrtho) + { + int repx = (p1.x () + p2.x ()) / 2; // central scan start + int repy = p1.y () - (int) ((repx - p1.x ()) * (p2.x () - p1.x ()) + / (p2.y () - p1.y ())); + return (new VHScannerO8 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, repx, repy)); + } + else return (new DirectionalScannerO8 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, p1.x (), p1.y ())); + } + else + { + if (isOrtho) + { + int repy = (p1.y () + p2.y ()) / 2; // central scan start + int repx = p1.x () - (int) ((repy - p1.y ()) * (p2.y () - p1.y ()) + / (p2.x () - p1.x ())); + return (new VHScannerO7 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, repx, repy)); + } + else return (new DirectionalScannerO7 (xmin, ymin, xmax, ymax, + a, b, c2, nbs, steps, p1.x (), p1.y ())); + } +} + + + +DirectionalScanner *ScannerProvider::getScanner ( + Pt2i p1, Pt2i p2, Pt2i v1, Pt2i v2, + int xmin, int ymin, int xmax, int ymax) +{ + // Get the scan strip center + int cx = (p1.x () + p2.x ()) / 2; + int cy = (p1.y () + p2.y ()) / 2; + + // Gets the steps position array + int nbs = 0; + bool *steps = v1.stepsTo (v2, &nbs); + + // Equation of the straight line passing through the center : ax - by = mu + int a = v2.y () - v1.y (); + int b = v1.x () - v2.x (); + if (a < 0 || (a == 0 && b < 0)) + { + a = -a; + b = -b; + } + + // Equation of the support lines of the scan strip + int c1 = a * p1.x () + b * p1.y (); + int c2 = a * p2.x () + b * p2.y (); + + // Builds and returns the appropriate scanner + if (b < 0) + if (-b > a) + return (new DirectionalScannerO1 (xmin, ymin, xmax, ymax, + a, b, c1, c2, nbs, steps, cx, cy)); + else + return (new DirectionalScannerO2 (xmin, ymin, xmax, ymax, + a, b, c1, c2, nbs, steps, cx, cy)); + else + if (b > a) + return (new DirectionalScannerO8 (xmin, ymin, xmax, ymax, + a, b, c1, c2, nbs, steps, cx, cy)); + else + return (new DirectionalScannerO7 (xmin, ymin, xmax, ymax, + a, b, c1, c2, nbs, steps, cx, cy)); +} + + +DirectionalScanner *ScannerProvider::getScanner ( + Pt2i centre, Vr2i normal, int length, + int xmin, int ymin, int xmax, int ymax) +{ + // Gets the steps position array + int nbs = 0; + bool *steps = normal.steps (&nbs); + + // Orients the direction rightwards + int a = normal.x (); + int b = normal.y (); // as equation is (ax - by) + if (a < 0 || (a == 0 && b < 0)) + { + a = -a; + b = -b; + } + + // Builds and returns the appropriate scanner + if (b < 0) + if (-b > a) + return (new DirectionalScannerO1 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + return (new DirectionalScannerO2 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + if (b > a) + return (new DirectionalScannerO8 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + return (new DirectionalScannerO7 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); +} + + +DirectionalScanner *ScannerProvider::getScanner ( + Pt2i centre, Vr2i normal, int length, bool controlable, + int xmin, int ymin, int xmax, int ymax) +{ + // Gets the steps position array + int nbs = 0; + bool *steps = normal.steps (&nbs); + + // Orients the direction rightwards + int a = normal.x (); + int b = normal.y (); // as equation is (ax - by) + if (a < 0 || (a == 0 && b < 0)) + { + a = -a; + b = -b; + } + + + if (b < 0) + if (-b > a) + return (controlable ? + (isOrtho ? + (DirectionalScanner *) + new VHScannerO1 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length) : + (DirectionalScanner *) + new DynamicalScannerO1 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)) : + (DirectionalScanner *) + new DirectionalScannerO1 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + return (controlable ? + (isOrtho ? + (DirectionalScanner *) + new VHScannerO2 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length) : + (DirectionalScanner *) + new DynamicalScannerO2 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)) : + (DirectionalScanner *) + new DirectionalScannerO2 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + if (b > a) + return (controlable ? + (isOrtho ? + (DirectionalScanner *) + new VHScannerO8 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length) : + (DirectionalScanner *) + new DynamicalScannerO8 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)) : + (DirectionalScanner *) + new DirectionalScannerO8 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); + else + return (controlable ? + (isOrtho ? + (DirectionalScanner *) + new VHScannerO7 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length) : + (DirectionalScanner *) + new DynamicalScannerO7 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)) : + (DirectionalScanner *) + new DirectionalScannerO7 (xmin, ymin, xmax, ymax, + a, b, nbs, steps, + centre.x (), centre.y (), length)); +} diff --git a/Code/Seg/DirectionalScanner/scannerprovider.h b/Code/Seg/DirectionalScanner/scannerprovider.h new file mode 100755 index 0000000000000000000000000000000000000000..3dd5be35b6603c3cabfa8feaddec612009b5407e --- /dev/null +++ b/Code/Seg/DirectionalScanner/scannerprovider.h @@ -0,0 +1,118 @@ +#ifndef SCANNER_PROVIDER_H +#define SCANNER_PROVIDER_H + +#include <cstdlib> +#include "directionalscanner.h" + +using namespace std; + + + +/** + * @class ScannerProvider scannerprovider.h + * \brief Directional scanner provider. + * Provides ad-hoc directional scanners in the relevant octant + * and according to static or dynamical needs. + * \author {P. Even} + */ +class ScannerProvider +{ + +public: + + /** + * @fn ScannerProvider() + * \brief Builds a directional scanner provider. + */ + ScannerProvider () : isOrtho (false) { } + + /** + * @fn getScanner(Pt2i p1, Pt2i p2, + * int xmin, int xmax, int ymin, int ymax) + * \brief Returns an incremental directional scanner. + * Returns a directional scanner from two control points. + * The scan strip is composed of parallel scan lines, the first one being + * defined by control points p1 and p2. + * @param p1 Start control point. + * @param p2 End control point. + * @param xmin Left border of the scan area. + * @param xmax Right border of the scan area. + * @param ymin Low border of the scan area. + * @param ymax Up border of the scan area. + */ + DirectionalScanner *getScanner (Pt2i p1, Pt2i p2, + int xmin, int xmax, int ymin, int ymax); + + /** + * @fn getScanner(Pt2i p1, Pt2i p2, Pt2i v1, Pt2i v2, + * int xmin, int ymin, nt xmax, int ymax) + * \brief Returns an incremental directional scanner. + * Returns a directional scanner from two points and direction v1 -> v2. + * The scan strip is composed of parallel scan lines, centered on the middle + * of (p1,p2) and aligned on (v1,v2). + * @param p1 start control point + * @param p2 end control point + * @param v1 direction start point + * @param v2 direction end point + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + */ + DirectionalScanner *getScanner (Pt2i p1, Pt2i p2, + Pt2i v1, Pt2i v2, + int xmin, int ymin, int xmax, int ymax); + + /** + * @fn getScanner(Pt2i centre, Vr2i normal, int length, + * int xmin, int ymin, nt xmax, int ymax) + * \brief Returns an incremental directional scanner. + * Returns a directional scanner from two points and direction v1 -> v2. + * The scan strip is composed of parallel scan lines, centered on the middle + * of (p1,p2) and aligned on (v1,v2). + * @param centre central point + * @param normal scan strip normal vector + * @param length length of a scan line + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + */ + DirectionalScanner *getScanner (Pt2i centre, Vr2i normal, int length, + int xmin, int ymin, int xmax, int ymax); + + /** + * @fn getScanner(Pt2i centre, Vr2i normal, int length, bool controlable, + * int xmin, int ymin, nt xmax, int ymax) + * \brief Returns an incremental directional scanner. + * Returns a directional scanner from two points and direction v1 -> v2. + * The scan strip is composed of parallel scan lines, centered on the middle + * of (p1,p2) and aligned on (v1,v2). + * @param centre central point + * @param normal scan strip normal vector + * @param length half length of a scan line + * @param controlable controlability request (true for a dynamical scanner) + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + */ + DirectionalScanner *getScanner (Pt2i centre, Vr2i normal, + int length, bool controlable, + int xmin, int ymin, int xmax, int ymax); + + /** + * @fn setOrtho(bool status) + * \brief Sets the orthogonal scanner modality. + * @param status new status for the orthogonal scanner modality. + */ + inline void setOrtho (bool status) { isOrtho = status; } + + +private: + + /** Orthogonal scanner modality. */ + bool isOrtho; +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/vhscannero1.cpp b/Code/Seg/DirectionalScanner/vhscannero1.cpp new file mode 100755 index 0000000000000000000000000000000000000000..0de21e9aa06c5d43e49ea8c2bdd292514ed4afed --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero1.cpp @@ -0,0 +1,188 @@ +#include <cstdlib> +#include <iostream> +#include "vhscannero1.h" + + + +VHScannerO1::VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DynamicalScannerO1 (xmin, ymin, xmax, ymax, + a, b, c, nbs, steps, sx, sy) +{ +} + + + +VHScannerO1::VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + // Looking for the central scan start position + do + { + lcy --; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +VHScannerO1::VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + for (int i = 0; i < w_2; i++) + { + lcy --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + while (w_2-- > 0) + { + cy++; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + +int VHScannerO1::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + + while (y < ymin && dla * x + dlb * y >= dlc2) + { + y++; + } + while (dla * x + dlb * y >= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO1::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcx --; + if (lcx < xmin) return 0; + + // Whenever the control line changed + while (lcy < ymax - 1 && dla * lcx + dlb * lcy > dlc1) + { + lcy ++; + } + while (lcy > ymin && dla * lcx + dlb * lcy < dlc1) + { + lcy --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + while (y < ymin && dla * x + dlb * y >= dlc2) + { + y ++; + } + while (dla * x + dlb * y >= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y ++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO1::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcx ++; + if (rcx >= xmax) return 0; + + while (rcy < ymax - 1 && dla * rcx + dlb * rcy > dlc1) + { + rcy ++; + } + while (rcy > ymin && dla * rcx + dlb * rcy < dlc1) + { + rcy --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + while (y < ymin && dla * x + dlb * y >= dlc2) + { + y++; + } + while (dla * x + dlb * y >= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y++; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/vhscannero1.h b/Code/Seg/DirectionalScanner/vhscannero1.h new file mode 100755 index 0000000000000000000000000000000000000000..9644a24fbc71ccd0d4a2650f9655d3d605c27ef2 --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero1.h @@ -0,0 +1,116 @@ +#ifndef VH_SCANNER_O1_H +#define VH_SCANNER_O1_H + +#include "dynamicalscannero1.h" + +using namespace std; + + + +/** + * @class VHScannerO1 vhscannero1.h + * \brief Vertical/horizontal directional scanner for the 1st octant. + * \author {P. Even} + */ +class VHScannerO1 : public DynamicalScannerO1 +{ + +public: + + /** + * @fn VHScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a vh scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn VHScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a vh scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn VHScanner01(int xmin, int ymin, int xmax, int ymax, + * int a, int b, *int nbs, bool *steps, + * int cx, int cy, int length) + * \brief Creates a vh scanner from pattern, center and length . + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param xmax right border of the scan area + * @param ymin low border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + VHScannerO1 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/vhscannero2.cpp b/Code/Seg/DirectionalScanner/vhscannero2.cpp new file mode 100755 index 0000000000000000000000000000000000000000..59eccb10625d031bddf2cec0d44df82e019d56cf --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero2.cpp @@ -0,0 +1,188 @@ +#include <cstdlib> +#include <iostream> +#include "vhscannero2.h" + + + +VHScannerO2::VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DynamicalScannerO2 (xmin, ymin, xmax, ymax, + a, b, c, nbs, steps, sx, sy) +{ +} + + + +VHScannerO2::VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + if (c2 > c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + // Looking for the central scan start position + do + { + lcx ++; + } + while (dla * lcx + dlb * lcy < c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +VHScannerO2::VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + for (int i = 0; i < w_2; i++) + { + lcx ++; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + while (w_2-- > 0) + { + cx--; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc1 - this->dlc2; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + +int VHScannerO2::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + + while (x >= xmax && dla * x + dlb * y >= dlc2) + { + x--; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + x--; + } + return ((int) (scan.size ())); +} + + +int VHScannerO2::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcy --; + if (lcy < ymin) return 0; + + // Whenever the control line changed + while (lcx > xmin && dla * lcx + dlb * lcy > dlc1) + { + lcx --; + } + while (lcx < xmax - 1 && dla * lcx + dlb * lcy < dlc1) + { + lcx ++; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + while (x >= xmax && dla * x + dlb * y >= dlc2) + { + x--; + } + while (dla * x + dlb * y >= dlc2 && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + x--; + } + return ((int) (scan.size ())); +} + + +int VHScannerO2::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcy ++; + if (rcy >= ymax) return 0; + + while (rcx > xmin && dla * rcx + dlb * rcy > dlc1) + { + rcx --; + } + while (rcx < xmax - 1 && dla * rcx + dlb * rcy < dlc1) + { + rcx ++; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + while ((y < ymin || x >= xmax) && dla * x + dlb * y >= dlc2) + { + x--; + } + while (dla * x + dlb * y >= dlc2 && y < ymax && x >= xmin) + { + scan.push_back (Pt2i (x, y)); + x--; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/vhscannero2.h b/Code/Seg/DirectionalScanner/vhscannero2.h new file mode 100755 index 0000000000000000000000000000000000000000..b0a8e8ae5c76d197ca52572295c57944961c18ba --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero2.h @@ -0,0 +1,116 @@ +#ifndef VH_SCANNER_O2_H +#define VH_SCANNER_O2_H + +#include "dynamicalscannero2.h" + +using namespace std; + + + +/** + * @class VHScannerO2 dynamicalscannero2.h + * \brief Vertical / horizontal directional scanner for the 2nd octant. + * \author {P. Even} + */ +class VHScannerO2 : public DynamicalScannerO2 +{ + +public: + + /** + * @fn VHScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a vh scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn VHScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a vh scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn VHScanner02(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int nbs, bool *steps, + * int cx, int cy, int length) + * \brief Creates a vh scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + VHScannerO2 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/vhscannero7.cpp b/Code/Seg/DirectionalScanner/vhscannero7.cpp new file mode 100755 index 0000000000000000000000000000000000000000..f46b883dbee1983b9bc07b306324e23bbe5862f9 --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero7.cpp @@ -0,0 +1,189 @@ +#include <cstdlib> +#include <iostream> +#include "vhscannero7.h" + + + +VHScannerO7::VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DynamicalScannerO7 (xmin, ymin, xmax, ymax, + a, b, c, nbs, steps, sx, sy) +{ +} + + + +VHScannerO7::VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + // Looking for the central scan start position + do + { + lcx --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +VHScannerO7::VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + for (int i = 0; i < w_2; i++) + { + lcx --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + while (w_2-- > 0) + { + cx++; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + + +int VHScannerO7::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + + while (x < xmin && dla * x + dlb * y <= dlc2) + { + x++; + } + while (dla * x + dlb * y <= dlc2 && x < xmax) + { + scan.push_back (Pt2i (x, y)); + x++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO7::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcy ++; + if (lcy >= ymax) return 0; + + while (lcx < xmax - 1 && dla * lcx + dlb * lcy < dlc1) + { + lcx ++; + } + while (lcx > xmin && dla * lcx + dlb * lcy > dlc1) + { + lcx --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + while (x < xmin && dla * x + dlb * y <= dlc2) + { + x++; + } + while (dla * x + dlb * y <= dlc2 && x < xmax) + { + scan.push_back (Pt2i (x, y)); + x++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO7::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcy --; + if (rcy < ymin) return 0; + + // Whenever the control corridor changed + while (rcx < xmax - 1 && dla * rcx + dlb * rcy < dlc1) + { + rcx ++; + } + while (rcx > xmin && dla * rcx + dlb * rcy > dlc1) + { + rcx --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + while (x < xmin && dla * x + dlb * y <= dlc2) + { + x++; + } + while (dla * x + dlb * y <= dlc2 && x < xmax) + { + scan.push_back (Pt2i (x, y)); + x++; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/vhscannero7.h b/Code/Seg/DirectionalScanner/vhscannero7.h new file mode 100755 index 0000000000000000000000000000000000000000..5ad1e12b4fae43fcecacdeb757bc0606857ff382 --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero7.h @@ -0,0 +1,116 @@ +#ifndef VH_SCANNER_O7_H +#define VH_SCANNER_O7_H + +#include "dynamicalscannero7.h" + +using namespace std; + + + +/** + * @class VHScannerO7 vhscannero7.h + * \brief Vertical / horizontal directional scanner for the 7th octant. + * \author {P. Even} + */ +class VHScannerO7 : public DynamicalScannerO7 +{ + +public: + + /** + * @fn VHScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a vh scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn VHScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a vh scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn VHScanner07(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int nbs, bool *steps, + int cx, int cy, int length) + * \brief Creates a vh scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + VHScannerO7 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +}; + +#endif diff --git a/Code/Seg/DirectionalScanner/vhscannero8.cpp b/Code/Seg/DirectionalScanner/vhscannero8.cpp new file mode 100755 index 0000000000000000000000000000000000000000..f6b8c612e83ac2456bbb015d9a75dbbe9e406c51 --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero8.cpp @@ -0,0 +1,189 @@ +#include <cstdlib> +#include <iostream> +#include "vhscannero8.h" + + + +VHScannerO8::VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy) + : DynamicalScannerO8 (xmin, ymin, xmax, ymax, + a, b, c, nbs, steps, sx, sy) +{ +} + + + +VHScannerO8::VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + if (c2 < c1) + { + this->dlc1 = c2; + this->dlc2 = c1; + c1 = c2; + } + else + { + this->dlc1 = c1; + this->dlc2 = c2; + } + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + // Looking for the central scan start position + do + { + lcy --; + } + while (dla * lcx + dlb * lcy > c1); + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; + fs = steps + nbs; +} + + + +VHScannerO8::VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length) +{ + this->xmin = xmin; + this->xmax = xmax; + this->ymin = ymin; + this->ymax = ymax; + this->nbs = nbs; + this->steps = steps; + lcx = cx; + lcy = cy; + rcx = cx; + rcy = cy; + this->dla = a; + this->dlb = b; + fs = steps + nbs; + int w_2 = (length + 1) / 2; + + // Looking for the central scan start position + for (int i = 0; i < w_2; i++) + { + lcy --; + } + dlc1 = dla * lcx + dlb * lcy; + + // Looking for the upper leaning line + while (w_2-- > 0) + { + cy++; + } + dlc2 = dla * cx + dlb * cy; + + this->templ_a = a; + this->templ_b = b; + this->templ_nu = this->dlc2 - this->dlc1; + + rcx = lcx; + rcy = lcy; + lst2 = steps; + rst2 = steps; +} + + + +int VHScannerO8::first (vector<Pt2i> &scan) +{ + int x = lcx, y = lcy; // Current position coordinates + + while (y < ymin && dla * x + dlb * y <= dlc2) + { + y++; + } + while (dla * x + dlb * y <= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO8::nextOnLeft (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + lcx --; + if (lcx < xmin) return 0; + + while (lcy < ymax - 1 && dla * lcx + dlb * lcy < dlc1) + { + lcy ++; + } + while (lcy > ymin && dla * lcx + dlb * lcy > dlc1) + { + lcy --; + } + + // Computes the next scan + int x = lcx; + int y = lcy; + while (y < ymin && dla * x + dlb * y <= dlc2) + { + y++; + } + while (dla * x + dlb * y <= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y++; + } + return ((int) (scan.size ())); +} + + +int VHScannerO8::nextOnRight (vector<Pt2i> &scan) +{ + // Prepares the next scan + scan.clear (); + rcx ++; + if (rcx >= xmax) return 0; + + // Whenever the control corridor changed + while (rcy < ymax - 1 && dla * rcx + dlb * rcy < dlc1) + { + rcy ++; + } + while (rcy > ymin && dla * rcx + dlb * rcy > dlc1) + { + rcy --; + } + + // Computes the next scan + int x = rcx; + int y = rcy; + while (y < ymin && dla * x + dlb * y <= dlc2) + { + y++; + } + while (dla * x + dlb * y <= dlc2 && y < ymax) + { + scan.push_back (Pt2i (x, y)); + y++; + } + return ((int) (scan.size ())); +} diff --git a/Code/Seg/DirectionalScanner/vhscannero8.h b/Code/Seg/DirectionalScanner/vhscannero8.h new file mode 100755 index 0000000000000000000000000000000000000000..e05ba493570648a83c1895c300a9bb432ec0593c --- /dev/null +++ b/Code/Seg/DirectionalScanner/vhscannero8.h @@ -0,0 +1,116 @@ +#ifndef VH_SCANNER_O8_H +#define VH_SCANNER_O8_H + +#include "dynamicalscannero8.h" + +using namespace std; + + + +/** + * @class VHScannerO8 vhscannero8.h + * \brief Vertical / horizontal directional scanner for the 8th octant. + * \author {P. Even} + */ +class VHScannerO8 : public DynamicalScannerO8 +{ + +public: + + /** + * @fn VHScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c, + * int nbs, bool *steps, int sx, int sy) + * \brief Creates a vh scanner from pattern, start and upper bound. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a start point, a line pattern, and an upper bound. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c y intercept parameter of the upper bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param sx abscissae of the central scan start point + * @param sy ordinate of the central scan start point + */ + VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c, + int nbs, bool *steps, int sx, int sy); + + /** + * @fn VHScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int c1, int c2, + * int nbs, bool *steps, int cx, int cy) + * \brief Creates a vh scanner from pattern, center and bounds. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, upper and lower bounds. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param c1 y intercept parameter of one of the bounds + * @param c2 y intercept parameter of the other bound + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + */ + VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int c1, int c2, + int nbs, bool *steps, int cx, int cy); + + /** + * @fn VHScanner08(int xmin, int ymin, int xmax, int ymax, + * int a, int b, int nbs, bool *steps, + * int cx, int cy, int length) + * \brief Creates a vh scanner from pattern, center and length. + * The scan strip is composed of parallel scan lines, the first one being + * defined by a center, a line pattern, and a length value. + * @param xmin left border of the scan area + * @param ymin low border of the scan area + * @param xmax right border of the scan area + * @param ymax up border of the scan area + * @param a x coefficient of the discrete support line + * @param b y coefficient of the discrete support line + * @param nbs size of the support line pattern + * @param steps support line pattern + * @param cx abscissae of the central scan center + * @param cy ordinate of the central scan center + * @param length length of a scan strip + */ + VHScannerO8 (int xmin, int ymin, int xmax, int ymax, + int a, int b, int nbs, bool *steps, + int cx, int cy, int length); + + /** + * @fn first(vector<Pt2i> &scan) + * \brief Returns the central scan. + * Fills in the vector with the central scan and returns its size. + * @param scan vectors of points to be filled in. + */ + int first (vector<Pt2i> &scan); + + /** + * @fn nextOnLeft(vector<Pt2i> &scan) + * \brief Returns the next scan on the left. + * Fills in the vector with the next scan on left and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnLeft (vector<Pt2i> &scan); + + /** + * @fn nextOnRight(vector<Pt2i> &scan) + * \brief Returns the next scan on the right. + * Fills in the vector with the next scan on right and returns its size. + * @param scan vectors of points to be filled in. + */ + int nextOnRight (vector<Pt2i> &scan); + +}; + +#endif diff --git a/Code/Seg/ImageTools/absrat.cpp b/Code/Seg/ImageTools/absrat.cpp new file mode 100755 index 0000000000000000000000000000000000000000..e5a7a8cd6b49977f23799e52e051315ec046d061 --- /dev/null +++ b/Code/Seg/ImageTools/absrat.cpp @@ -0,0 +1,44 @@ +#include "absrat.h" + + +AbsRat::AbsRat () +{ + num = 1; + den = 0; +} + + +AbsRat::AbsRat (int numerator, int denominator) +{ + num = (numerator < 0 ? - numerator : numerator); + den = (denominator < 0 ? - denominator : denominator); +} + + +AbsRat::AbsRat (const AbsRat &rat) +{ + num = rat.num; + den = rat.den; +} + + +void AbsRat::attractsTo (const AbsRat &val, const AbsRat &ratio) +{ + if (val.den != 0) + num = (ratio.den * num * val.den + - (num * val.den - den * val.num) * ratio.num) + / (ratio.den * val.den); +} + + +void AbsRat::sticksTo (const AbsRat &val) +{ + if (val.den != 0) num = (den * val.num) / val.den; +} + + +void AbsRat::mult (const AbsRat &val) +{ + num *= val.num; + den *= val.den; +} diff --git a/Code/Seg/ImageTools/absrat.h b/Code/Seg/ImageTools/absrat.h new file mode 100755 index 0000000000000000000000000000000000000000..a52e710687322f77120e679931e9e7c86c93767f --- /dev/null +++ b/Code/Seg/ImageTools/absrat.h @@ -0,0 +1,162 @@ +#ifndef ABSOLUTE_RATIONAL_H +#define ABSOLUTE_RATIONAL_H + + +using namespace std; + + + +/** + * @class AbsRat absrat.h + * \brief Absolute rational number. + * + * This absolute number may have a null denominator. + * It should not be evaluated. + * It is mostly intended to comparison operations. + * \author {P. Even} + */ +class AbsRat +{ + +public: + + /** + * @fn AbsRat () + * \brief Creates a null absolute rational number. + */ + AbsRat (); + + /** + * @fn AbsRat (int numerator, int denominator) + * \brief Creates a absolute rational number from numerator and denominator. + * @param numerator Numerator. + * @param denominator Denominator. + */ + AbsRat (int numerator, int denominator); + + /** + * @fn AbsRat (const AbsRat &rat) + * \brief Creates a absolute rational number from another one. + * @param rat The rational number to copy. + */ + AbsRat (const AbsRat &rat); + + /** + * @fn ~AbsRatRational () + * \brief Deletes the absolute rational number. + */ + ~AbsRat () { } + + /** + * @fn int numerator () + * \brief Returns the numerator of the absolute rational number. + */ + inline int numerator () const { return num; } + + /** + * @fn int denominator () + * \brief Returns the denominator of the absolute rational number. + */ + inline int denominator () const { return den; } + + /** + * @fn void set (const AbsRat &val) + * \brief Sets the value of the rational number. + * @value val New value of the rational number. + */ + inline void set (const AbsRat &val) { num = val.num; den = val.den; } + + /** + * @fn void set (int val) + * \brief Sets the value of the rational number. + * @value val New value of the rational number. + */ + inline void set (int val) { num = val; den = 1; } + + /** + * @fn void set (int numerator, int denominator) + * \brief Sets the value of the rational number. + * @value numerator New numerator of the rational number. + * @value denominator New denominator of the rational number. + */ + inline void set (int numerator, int denominator) { + num = numerator; den = denominator; } + + /** + * @fn bool equals (const AbsRat &r) + * \brief Checks equivalence to the given rational number. + * @param r the given rational number. + */ + inline bool equals (const AbsRat &r) const { + return (num * r.den == den * r.num); } + + /** + * @fn bool lessThan (const AbsRat &r) + * \brief Checks if the rational number is strictly less than given one. + * @param r the given rational number. + */ + inline bool lessThan (const AbsRat &r) const { + return (num * r.den < den * r.num); } + + /** + * @fn bool lessEqThan (const AbsRat &r) + * \brief Checks if the rational number is less or equal to given one. + * @param r the given rational number. + */ + inline bool lessEqThan (const AbsRat &r) const { + return (num * r.den <= den * r.num); } + + /** + * @fn bool greaterThan (const AbsRat &r) + * \brief Checks if the rational number is strictly greater than given one. + * @param r the given rational number. + */ + inline bool greaterThan (const AbsRat &r) const { + return (num * r.den > den * r.num); } + + /** + * @fn bool greaterEqThan (const AbsRat &r) + * \brief Checks if the rational number is greater or equal to given one. + * @param r the given rational number. + */ + inline bool greaterEqThan (const AbsRat &r) const { + return (num * r.den >= den * r.num); } + + /** + * @fn void attractsTo (const AbsRat &val, const AbsRat &ratio) + * \brief Attracts the rational number to the given one with given ratio. + * Withdraws to the rational number + * the difference between the rational number and an attractor number + * multiplied by the ratio. + * The denominator is left unchanged. + * @param val Goal rational value. + * @param ratio Attraction ratio. + */ + void attractsTo (const AbsRat &val, const AbsRat &ratio); + + /** + * @fn void sticksTo (const AbsRat &val) + * \brief Sets the rational number to the given one value. + * The denominator is left unchanged. + * @param val Given rational number. + */ + void sticksTo (const AbsRat &val); + + /** + * @fn void mult (const AbsRat &val) + * \brief Multiplies the rational number by the given one. + * @param val Given rational number. + */ + void mult (const AbsRat &val); + + +protected: + + /** Positive numerator of the rational number. */ + int num; + /** Positive denominator of the rational number (might be null). */ + int den; + +}; + +#endif diff --git a/Code/Seg/ImageTools/digitalstraightline.cpp b/Code/Seg/ImageTools/digitalstraightline.cpp new file mode 100755 index 0000000000000000000000000000000000000000..db1f446e568853184d0ac5ae9d2c185d62fca915 --- /dev/null +++ b/Code/Seg/ImageTools/digitalstraightline.cpp @@ -0,0 +1,452 @@ +#include <cstdlib> +#include <iostream> +#include "digitalstraightline.h" + + +const int DigitalStraightLine::DSL_THIN = 1; +const int DigitalStraightLine::DSL_NAIVE = 2; +const int DigitalStraightLine::DSL_STANDARD = 3; + + +DigitalStraightLine::DigitalStraightLine (int a, int b, int c, int nu) +{ + this->a = a; + this->b = b; + if (nu < 0) + { + this->c = c + 1 + nu; + this->nu = - nu; + } + else + { + this->c = c; + this->nu = nu; + } + if (a < 0) + { + this->a = - this->a; + this->b = - this->b; + this->c = 1 - c - nu; + } + else if (a == 0 && b < 0) + { + this->b = - this->b; + this->c = 1 - c - nu; + } + int pg = pgcd (a, (b < 0 ? - b : b)); + if (pg != 1) + { + a /= pg; + b /= pg; + c /= pg; + nu /= pg; + } +} + + +DigitalStraightLine::DigitalStraightLine (Pt2i p1, Pt2i p2, int type) +{ + if (p1.y () < p2.y ()) + { + a = p2.y () - p1.y (); + b = p1.x () - p2.x (); + } + else + { + a = p1.y () - p2.y (); + b = p2.x () - p1.x (); + if (a == 0 && b < 0) b = -b; + } + int pg = pgcd (a, b < 0 ? -b : b); + a /= pg; + b /= pg; + c = a * p1.x () + b * p1.y (); + + if (type == DSL_NAIVE) + { + nu = b; + if (nu < 0) nu = -nu; + if (nu < a) nu = a; + // To match the Pt2i::stepsTo method ... + if ((b > 0 && a > b) || (b < 0 && a < -b)) + c -= (nu - 1) / 2; + else + c -= nu / 2; + } + + else if (type == DSL_STANDARD) + { + nu = a + (b < 0 ? -b : b); + if ((b > 0 && a > b) || (b < 0 && a < -b)) + c -= (nu - 1) / 2; + else + c -= nu / 2; + } + + else // type == DSL_THIN + nu = 1; +} + + +DigitalStraightLine::DigitalStraightLine (Pt2i p1, Pt2i p2, Pt2i p3) +{ + if (p1.y () < p2.y ()) + { + a = p2.y () - p1.y (); + b = p1.x () - p2.x (); + } + else + { + a = p1.y () - p2.y (); + b = p2.x () - p1.x (); + if (a == 0 && b < 0) b = -b; + } + int pg = pgcd (a, b < 0 ? -b : b); + a /= pg; + b /= pg; + c = a * p1.x () + b * p1.y (); + int d = a * p3.x () + b * p3.y (); + if (d < c) + { + nu = c - d + 1; + c = d; + } + else nu = d - c + 1; +} + + +DigitalStraightLine::DigitalStraightLine (Pt2i p1, Pt2i p2, + int type, int atRight) +{ + if (p1.y () < p2.y ()) + { + a = p2.y () - p1.y (); + b = p1.x () - p2.x (); + } + else + { + a = p1.y () - p2.y (); + b = p2.x () - p1.x (); + if (a == 0 && b < 0) b = -b; + } + int pg = pgcd (a, b < 0 ? -b : b); + a /= pg; + b /= pg; + c = a * p1.x () + b * p1.y (); + + int bb = (b < 0 ? -b : b); + c += atRight * (a < bb ? bb : a); + + if (type == DSL_NAIVE) + { + nu = b; + if (nu < 0) nu = -nu; + if (nu < a) nu = a; + // To match the Pt2i::stepsTo method ... + if ((b > 0 && a > b) || (b < 0 && a < -b)) + c -= (nu - 1) / 2; + else + c -= nu / 2; + } + + else if (type == DSL_STANDARD) + { + nu = a + (b < 0 ? -b : b); + if ((b > 0 && a > b) || (b < 0 && a < -b)) + c -= (nu - 1) / 2; + else + c -= nu / 2; + } + + else // type == DSL_THIN + nu = 1; +} + + +DigitalStraightLine::DigitalStraightLine (const DigitalStraightLine &l) +{ + a = l.a; + b = l.b; + c = l.c; + nu = l.nu; +} + + +int DigitalStraightLine::manhattan (Pt2i pix) const +{ + int absb = b < 0 ? -b : b; + int per = (a < absb ? absb : a); + int pos = a * pix.x () + b * pix.y () - c; + if (pos < 0) return ((pos + 1 - per) / per); + else if (pos >= nu) return ((pos + per - nu) / per); + else return 0; +} + + +Pt2i DigitalStraightLine::getABoundingPoint (bool upper) const +{ + int sa = a, sb = b, u1 = 1, v1 = 0, u2 = 0, v2 = 1; + while (sb != 0) + { + int r = sa % sb; + int q = sa / sb; + int u3 = u1 - q * u2; + int v3 = v1 - q * v2; + u1 = u2; + v1 = v2; + u2 = u3; + v2 = v3; + sa = sb; + sb = r; + } + if (sa < 0) // should be 1 or -1 if a and b are primal + { + u1 = - u1; + v1 = - v1; + } + return (upper ? Pt2i (u1 * (c + nu - 1), v1 * (c + nu - 1)) + : Pt2i (u1 * c, v1 * c)); +} + + +void DigitalStraightLine::adjustWorkArea (int &xmin, int &ymin, + int &width, int &height) const +{ + (void) xmin; + (void) ymin; + (void) width; + (void) height; +} + + +void DigitalStraightLine::getBounds (vector<Pt2i> &bound, + int xmin, int ymin, int width, int height) const +{ + getBoundPoints (bound, false, xmin, ymin, width, height); + if (nu > period ()) getBoundPoints (bound, true, xmin, ymin, width, height); +} + + +void DigitalStraightLine::getBoundPoints (vector<Pt2i> &points, bool opposite, + int xmin, int ymin, int width, int height) const +{ + if (opposite && nu < period ()) return; + + int x, y, dec, r; + Pt2i pb = getABoundingPoint (opposite); + adjustWorkArea (xmin, ymin, width, height); + + if (b > 0) // downwards + if (b >= a) // rather horizontal (8th octant) + { + x = pb.x (); + y = pb.y (); + dec = x <= xmin ? (xmin - x) / b : (xmin - x) / b - 1; + x += dec * b; + y -= dec * a; + r = (opposite ? b - 1 : 0); + + while (x < xmin) + { + x++; + r += a; if (r >= b) + { + y --; + r -= b; + } + } + if ((opposite || r < nu ) && y >= ymin && y < ymin + height) + points.push_back (Pt2i (x, y)); + while (++x < xmin + width) + { + r += a; + if (r >= b) + { + y --; + r -= b; + } + if ((opposite || r < nu) && y >= ymin && y < ymin + height) + points.push_back (Pt2i (x, y)); + } + } + else // rather vertical (7th octant) + { + x = pb.x (); + y = pb.y (); + dec = y >= ymin + height ? (y - ymin - height) / a + : (y - ymin - height) / a - 1; + x += dec * b; + y -= dec * a; + r = (opposite ? 0 : a - 1); + + while (y >= ymin + height) + { + y--; + r += b; + if (r >= a) + { + x ++; + r -= a; + } + } + + if ((opposite || r >= a - nu) && x >= xmin && x < xmin + width) + points.push_back (Pt2i (x, y)); + while (y-- > ymin) + { + r += b; + if (r >= a) + { + x ++; + r -= a; + } + if ((opposite || r >= a - nu) && x >= xmin && x < xmin + width) + points.push_back (Pt2i (x, y)); + } + } + else // upwards + if (-b >= a) // rather horizontal (1st octant) + { + x = pb.x (); + y = pb.y (); + dec = x <= xmin ? (x - xmin) / b : (x - xmin) / b - 1; + x -= dec * b; + y += dec * a; + r = (opposite ? b + 1 : 0); + + while (x < xmin) + { + x++; + r -= a; + if (r <= b) + { + y ++; + r -= b; + } + } + + if ((opposite || r > -nu) && y >= ymin && y < ymin + height) + points.push_back (Pt2i (x, y)); + while (++x < xmin + width) + { + r -= a; + if (r <= b) + { + y ++; + r -= b; + } + if ((opposite || r > -nu) && y >= ymin && y < ymin + height) + points.push_back (Pt2i (x, y)); + } + } + else // rather vertical (2nd octant) + { + x = pb.x (); + y = pb.y (); + dec = y > ymin ? (ymin - y) / a - 1 : (ymin - y) / a; + x -= dec * b; + y += dec * a; + r = (opposite ? 0 : a - 1); + + while (y < ymin) + { + y++; + r -= b; + if (r >= a) + { + x ++; + r -= a; + } + } + + if ((opposite || r >= a - nu) && x >= xmin && x < xmin + width) + points.push_back (Pt2i (x, y)); + while (++y < ymin + height) + { + r -= b; + if (r >= a) + { + x ++; + r -= a; + } + if ((opposite || r >= a - nu) && x >= xmin && x < xmin + width) + points.push_back (Pt2i (x, y)); + } + } +} + + +const Pt2i DigitalStraightLine::centerOfIntersection ( + DigitalStraightLine *l) const +{ + int den = a * l->b - b * l->a; + if (den == 0) return (Pt2i (0, 0)); + return (Pt2i ( + ((c + nu / 2) * l->b - b * (l->c + l->nu / 2) + den / 2) / den, + (a * (l->c + l->nu / 2) - (c + nu / 2) * l->a + den / 2) / den)); +} + + +const Pt2i DigitalStraightLine::centerOfIntersection ( + Pt2i p1, Pt2i p2) const +{ + int sa = p2.y () - p1.y (); + int sb = p1.x () - p2.x (); + if (sa == 0) + { + if (sb == 0) return (Pt2i (0, 0)); + if (sb < 0) sb = -sb; + } + if (sa < 0) + { + sa = -sa; + sb = -sb; + } + int pg = pgcd (sa, sb < 0 ? -sb : sb); + sa /= pg; + sb /= pg; + + int den = a * sb - b * sa; + if (den == 0) return (Pt2i (0, 0)); + int sc = sa * p1.x () + sb * p1.y (); + return (Pt2i (((c + nu / 2) * sb - b * sc + den / 2) / den, + (a * sc - (c + nu / 2) * sa + den / 2) / den)); +} + + +bool DigitalStraightLine::owns (const Pt2i &p) const +{ + int val = a * p.x () + b * p.y () - c; + return (val >= 0 && val < nu); +} + + +bool DigitalStraightLine::owns (const Pt2i &p1, const Pt2i &p2) const +{ + int val1 = a * p1.x () + b * p1.y () - c; + int val2 = a * p2.x () + b * p2.y () - c; + return (val1 < val2 ? val1 <= 0 && val2 < nu + : val2 <= 0 && val1 < nu); +} + + +bool DigitalStraightLine::crosses (const Pt2i &p1, const Pt2i &p2) const +{ + int val1 = a * p1.x () + b * p1.y () - c; + int val2 = a * p2.x () + b * p2.y () - c; + return (val1 < val2 ? val2 >= 0 && val1 < nu + : val1 >= 0 && val2 < nu); +} + + +int DigitalStraightLine::pgcd (int a, int b) +{ + int r; + while (b != 0) + { + r = a % b; + a = b; + b = r; + } + return (a); +} diff --git a/Code/Seg/ImageTools/digitalstraightline.h b/Code/Seg/ImageTools/digitalstraightline.h new file mode 100755 index 0000000000000000000000000000000000000000..8a5c518406663e73b26bec0e68dc221692c99985 --- /dev/null +++ b/Code/Seg/ImageTools/digitalstraightline.h @@ -0,0 +1,239 @@ +#ifndef DIGITAL_STRAIGHT_LINE_H +#define DIGITAL_STRAIGHT_LINE_H + + +#include <vector> +#include "pt2i.h" + +using namespace std; + + +/** + * @class DigitalStraightLine digitalstraightline.h + * \brief Digital straight line. + * The digital straightline is defined by equation : c <= ax + by < c + nu + * where a >= 0, b > 0 if a = 0, and nu > 0. + * \author {P. Even} + */ +class DigitalStraightLine +{ + +public: + + /** Digital line type : Thin line (width = 1). */ + static const int DSL_THIN; + /** Digital line type : Naive line (width = max (|a|,|b|)). */ + static const int DSL_NAIVE; + /** Digital line type : Standard line (width = |a|+|b|). */ + static const int DSL_STANDARD; + + /** + * Creates a digital straightline from its equation parameters. + * @param a X value slope parameter of equation : c <= ax + by < c + nu + * @param b Y value slope parameter of equation : c <= ax + by < c + nu + * @param c Intercept parameter of equation : c <= ax + by < c + nu + * @param nu Width parameter of equation : c <= ax + by < c + nu + */ + DigitalStraightLine (int a, int b, int c, int nu); + + /** + * Creates a digital straight line bounded by the line joining two points. + * @param p1 First point on the bounding line. + * @param p2 Second point on the bounding line. + * @param type Digital line type : DSL_THIN, DSL_NAIVE or DSL_STANDARD. + */ + DigitalStraightLine (Pt2i p1, Pt2i p2, int type); + + /** + * Creates a digital straight line from an antipodal pair. + * @param p1 First segment end of the antipodal pair. + * @param p1 Second segment end of the antipodal pair. + * @param p3 Opposite point of the antipodal pair. + */ + DigitalStraightLine (Pt2i p1, Pt2i p2, Pt2i p3); + + /** + * Creates a digital straight line parallel to the line joining two points. + * Unknown use, care the side correctness in case of. + * @param p1 First point. + * @param p2 Second point. + * @param type Digital line type : DSL_THIN, DSL_NAIVE or DSL_STANDARD + * @param atRight Rightwards parallel offset to p1p2 line. + */ + DigitalStraightLine (Pt2i p1, Pt2i p2, int type, int atRight); + + /** + * Creates a similar digital straight line to an other one. + * @param l The original line. + */ + DigitalStraightLine (const DigitalStraightLine &l); + + /** + * Deletes the digital straight line. + */ + virtual ~DigitalStraightLine () { } + + /** + * \brief Fills in the given array with the three medial axis parameters. + * @param a X slope parameter to provide. + * @param b Y slope parameter to provide. + * @param c0 intercept parameter of the median axis. + */ + inline void getMedialAxis (int &a, int &b, int &c0) const + { + a = this->a; + b = this->b; + c0 = c + nu / 2; + } + + /** + * \brief Returns the period of the digital straight line : max (|a|,|b|). + */ + inline int period () const + { + int absb = b < 0 ? -b : b; + return (a < absb ? absb : a); + } + + /** + * \brief Returns the lower of the digital straight line : min (|a|,|b|). + */ + inline int antiperiod () const + { + int absb = b < 0 ? -b : b; + return (a < absb ? a : absb); + } + + /** + * \brief Returns the manhattan width of the digital straight line. + */ + inline int width () const + { + return (nu); + } + + /** + * \brief Returns a support vector of the digital straight line. + */ + inline Vr2i supportVector () const + { + return (Vr2i (-b, a)); + } + + /** + * \brief Returns the signed manhattan distance to the given point. + * Right values are provided for points on the right side (yp > line (yp)) + */ + int manhattan (Pt2i pix) const; + + /** + * \brief Returns a bounding point of the digital straight line. + * @param upper true for a upper bounding point, false for a lower one. + */ + virtual Pt2i getABoundingPoint (bool upper) const; + + /** + * \brief Fills in the vector with the requested bounding line points. + * The bounding line is naive if (nu < period). + * Restricts the points in the specified area. + * @param bound The vector of points to fill in. + * @param opposite true to get the opposite bounding line (ax+by=c+nu-1). + * @param xmin X coordinate of the viewport. + * @param ymin Y coordinate of the viewport. + * @param width Width of the viewport. + * @param height Height of the viewport. + */ + void getBoundPoints (vector<Pt2i> &bound, bool opposite, + int xmin, int ymin, int width, int height) const; + + /** + * \brief Fills in the provided vector with the digital line bounding points. + * @param bound The vector of points to fill in. + * @param xmin X coordinate of the viewport. + * @param ymin Y coordinate of the viewport. + * @param width Width of the viewport. + * @param height Height of the viewport. + */ + void getBounds (vector<Pt2i> &bound, + int xmin, int ymin, int width, int height) const; + + /** + * \brief Checks if given point belongs to the digital line. + * @param p Given point. + */ + bool owns (const Pt2i &p) const; + + /** + * \brief Checks if given segment entirely belongs to the digital line. + * @param p1 Given segment start point. + * @param p2 Given segment end point. + */ + bool owns (const Pt2i &p1, const Pt2i &p2) const; + + /** + * \brief Checks if given segment belongs even partially to the digital line. + * @param p1 Given segment start point. + * @param p2 Given segment end point. + */ + bool crosses (const Pt2i &p1, const Pt2i &p2) const; + + /** + * \brief Returns the center of the intersection with an other digital line. + * @param l The other digital line. + */ + const Pt2i centerOfIntersection (DigitalStraightLine *l) const; + + /** + * \brief Returns the center of the intersection with line P1-P2. + * Care : returns (0,0) if p1 == p2 or if P1P2 is parallel to the line. + * @param p1 = start point of the crossed line. + * @param p2 = end point of the crossed line. + */ + const Pt2i centerOfIntersection (Pt2i p1, Pt2i p2) const; + + +protected: + + /** + * \brief X value of the slope parameter in equation : c <= ax + by < c + nu. + * a is greater or equal to 0. + */ + int a; + + /** + * \brief Y value of the slope parameter in equation : c <= ax + by < c + nu. + * b is a positive value when the line is horizontal (a = 0). + */ + int b; + + /** + * \brief Intercept parameter in equation : c <= ax + by < c + nu. + */ + int c; + + /** + * \brief Width parameter in equation : c <= ax + by < c + nu. + * nu is a positive value. + */ + int nu; + + + /** + * \brief Adjusts the provided area on the straight line limits. + * @param xmin Left coordinate of the area. + * @param ymin Bottom coordinate of the area. + * @param width Width of the area. + * @param height Height of the area. + */ + virtual void adjustWorkArea (int &xmin, int &ymin, + int &width, int &height) const; + + /** + * \brief Returns the greater common divisor between two integer values. + * @param a First integer. + * @param b Second integer. + */ + static int pgcd (int a, int b); + +}; +#endif diff --git a/Code/Seg/ImageTools/digitalstraightsegment.cpp b/Code/Seg/ImageTools/digitalstraightsegment.cpp new file mode 100755 index 0000000000000000000000000000000000000000..212aadc363bfba23462aa0f745a8abe1bd40f766 --- /dev/null +++ b/Code/Seg/ImageTools/digitalstraightsegment.cpp @@ -0,0 +1,103 @@ +#include <iostream> +#include "digitalstraightsegment.h" + + + +DigitalStraightSegment::DigitalStraightSegment (Pt2i p1, Pt2i p2, int type, + int xmin, int ymin, int xmax, int ymax) + : DigitalStraightLine (p1, p2, type) +{ + if (a < (b < 0 ? -b : b)) + { + min = xmin; + max = xmax; + } + else + { + min = ymin; + max = ymax; + } +} + + +DigitalStraightSegment::DigitalStraightSegment (Pt2i p1, Pt2i p2, Pt2i p3, + int xmin, int ymin, int xmax, int ymax) + : DigitalStraightLine (p1, p2, p3) +{ + if (a < (b < 0 ? -b : b)) + { + min = xmin; + max = xmax; + } + else + { + min = ymin; + max = ymax; + } +} + + +Pt2i DigitalStraightSegment::getABoundingPoint (bool upper) const +{ + int sa = a, sb = b, u1 = 1, v1 = 0, u2 = 0, v2 = 1; + while (sb != 0) + { + int r = sa % sb; + int q = sa / sb; + int u3 = u1 - q * u2; + int v3 = v1 - q * v2; + u1 = u2; + v1 = v2; + u2 = u3; + v2 = v3; + sa = sb; + sb = r; + } + if (sa < 0) // should be 1 or -1 if a and b are primal + { + u1 = - u1; // necessary if sa = -1 + v1 = - v1; + } + Pt2i extr (upper ? Pt2i (u1 * (c + nu - 1), v1 * (c + nu - 1)) + : Pt2i (u1 * c, v1 * c)); + + int dec = 0; + int bb = (b < 0 ? -b : b); + if (a < bb) + { + if (extr.x () > max) + dec = 1 + (extr.x () - max) / bb; + else if (extr.x () < min) + dec = -1 - (min - extr.x ()) / bb; + if (b < 0) dec = -dec; + } + else + { + if (extr.y () > max) + dec = -1 - (extr.y () - max) / a; + else if (extr.y () < min) + dec = 1 + (min - extr.y ()) / a; + } + extr.set (extr.x () - dec * b, extr.y () + dec * a); + return extr; +} + + +void DigitalStraightSegment::adjustWorkArea (int &xmin, int &ymin, + int &width, int &height) const +{ + if (b > a || -b > a) + { + if (xmin < min) xmin = min; + // Caution, segment max limit is excluded + int x2 = (xmin + width < max + 1 ? xmin + width : max + 1); + width = (xmin >= x2 ? 0 : x2 - xmin); + } + else + { + if (ymin < min) ymin = min; + // Cochon, segment max limit is excluded + int y2 = (ymin + height < max + 1 ? ymin + height : max + 1); + height = (ymin >= y2 ? 0 : y2 - ymin); + } +} diff --git a/Code/Seg/ImageTools/digitalstraightsegment.h b/Code/Seg/ImageTools/digitalstraightsegment.h new file mode 100755 index 0000000000000000000000000000000000000000..21c2074532e9208b244abfd58680de8cf3af3894 --- /dev/null +++ b/Code/Seg/ImageTools/digitalstraightsegment.h @@ -0,0 +1,85 @@ +#ifndef DIGITAL_STRAIGHT_SEGMENT_H +#define DIGITAL_STRAIGHT_SEGMENT_H + + +#include "digitalstraightline.h" + +using namespace std; + + +/** + * @class DigitalStraightSegment digitalstraightsegment.h + * \brief Digital straight segment is a bounded digital straight line. + * The digital straightline is defined by equation : c <= ax + by < c + nu + * where a >= 0 and nu > 0. Seemingly b is unconstrained when a = 0. + * The bounds are horizontal lines (min <= y <= max) if the segment is vertical, + * vertical lines (min <= x <= max) if the segment is horizontal. + * The bound points belong to the digital straight segment. + * \author {P. Even} + */ +class DigitalStraightSegment : public DigitalStraightLine +{ +public: + + /** + * Creates a null-thick segment joining two points. + * @param p1 First point on the line. + * @param p2 Second point on the line. + * @param type Digital line type : DSL_THIN, DSL_NAIVE or DSL_STANDARD. + * @param xmin Segment min X coordinate. + * @param ymin Segment min Y coordinate. + * @param xmax Segment max X coordinate. + * @param ymax Segment max Y coordinate. + */ + DigitalStraightSegment (Pt2i p1, Pt2i p2, int type, + int xmin, int ymin, int xmax, int ymax); + + /** + * Creates a digital straight segment from an antipodal pair. + * @param p1 First segment end of the antipodal pair. + * @param p1 Second segment end of the antipodal pair. + * @param p3 Opposite point of the antipodal pair. + * @param xmin Segment min X coordinate. + * @param ymin Segment min Y coordinate. + * @param xmax Segment max X coordinate. + * @param ymax Segment max Y coordinate. + */ + DigitalStraightSegment (Pt2i p1, Pt2i p2, Pt2i p3, + int xmin, int ymin, int xmax, int ymax); + + /** + * \brief Returns a bounding point of the digital line + * @param upper true for a upper bounding point, false for a lower one. + */ + Pt2i getABoundingPoint (bool upper) const; + + +protected: + + /** + * \brief Bounding line lower coordinate. + * If the segment is stictly horizontal, it enforces x >= xmin. + * If the segment is vertical or diagonal, it enforces y >= ymin. + * The bound belongs to the segment. + */ + int min; + /** + * \brief Bounding line upper coordinate. + * If the segment is stictly horizontal, it enforces x <= xmax. + * If the segment is vertical or diagonal, it enforces y <= ymax. + * The bound belongs to the segment. + */ + int max; + + + /** + * \brief Adjusts the provided area on the segment limits. + * @param xmin Left coordinate of the area. + * @param ymin Bottom coordinate of the area. + * @param width Width of the area. + * @param height Height of the area. + */ + void adjustWorkArea (int &xmin, int &ymin, int &width, int &height) const; + +}; +#endif diff --git a/Code/Seg/ImageTools/pt2i.cpp b/Code/Seg/ImageTools/pt2i.cpp new file mode 100755 index 0000000000000000000000000000000000000000..2da369428315247f8df2f5708566e80c3524a155 --- /dev/null +++ b/Code/Seg/ImageTools/pt2i.cpp @@ -0,0 +1,794 @@ +#include "pt2i.h" +#include <cmath> + + +Pt2i::Pt2i () +{ + xp = 0; + yp = 0; +} + + +Pt2i::Pt2i (int x, int y) +{ + xp = x; + yp = y; +} + + +Pt2i::Pt2i (const Pt2i &p) +{ + xp = p.xp; + yp = p.yp; +} + + +Vr2i Pt2i::vectorTo (Pt2i p) const +{ + return (Vr2i (p.xp - xp, p.yp - yp)); +} + + +AbsRat Pt2i::triangleRationalHeight (const Pt2i &p1, const Pt2i &p2) const +{ + AbsRat xh = triangleXRationalHeight (p1, p2); + AbsRat yh = triangleYRationalHeight (p1, p2); + return (xh.lessThan (yh) ? xh : yh); +} + + +AbsRat Pt2i::triangleXRationalHeight (const Pt2i &p1, const Pt2i &p2) const +{ + int ax, ay, bx, by, cx, cy; + if (xp < p1.xp) + if (xp < p2.xp) + { + ax = xp; + ay = yp; + if (p1.xp < p2.xp) + { + bx = p1.xp; + by = p1.yp; + cx = p2.xp; + cy = p2.yp; + } + else + { + bx = p2.xp; + by = p2.yp; + cx = p1.xp; + cy = p1.yp; + } + } + else + { + ax = p2.xp; + ay = p2.yp; + bx = xp; + by = yp; + cx = p1.xp; + cy = p1.yp; + } + else + if (xp < p2.xp) + { + ax = p1.xp; + ay = p1.yp; + bx = xp; + by = yp; + cx = p2.xp; + cy = p2.yp; + } + else { + cx = xp; + cy = yp; + if (p1.xp < p2.xp) + { + ax = p1.xp; + ay = p1.yp; + bx = p2.xp; + by = p2.yp; + } + else + { + ax = p2.xp; + ay = p2.yp; + bx = p1.xp; + by = p1.yp; + } + } + return (AbsRat ((bx - ax) * (cy - ay) - (by - ay) * (cx - ax), cx - ax)); +} + + +AbsRat Pt2i::triangleYRationalHeight (const Pt2i &p1, const Pt2i &p2) const +{ + int ax, ay, bx, by, cx, cy; + if (yp < p1.yp) + if (yp < p2.yp) + { + ax = xp; + ay = yp; + if (p1.yp < p2.yp) + { + bx = p1.xp; + by = p1.yp; + cx = p2.xp; + cy = p2.yp; + } + else + { + bx = p2.xp; + by = p2.yp; + cx = p1.xp; + cy = p1.yp; + } + } + else + { + ax = p2.xp; + ay = p2.yp; + bx = xp; + by = yp; + cx = p1.xp; + cy = p1.yp; + } + else + if (yp < p2.yp) + { + ax = p1.xp; + ay = p1.yp; + bx = xp; + by = yp; + cx = p2.xp; + cy = p2.yp; + } + else { + cx = xp; + cy = yp; + if (p1.yp < p2.yp) + { + ax = p1.xp; + ay = p1.yp; + bx = p2.xp; + by = p2.yp; + } + else + { + ax = p2.xp; + ay = p2.yp; + bx = p1.xp; + by = p1.yp; + } + } + return (AbsRat ((bx - ax) * (cy - ay) - (by - ay) * (cx - ax), cy - ay)); +} + + +Pt2i *Pt2i::drawing (const Pt2i p, int *n) const +{ + int x1, y1, x2, y2; + if (xp > p.xp) + { + x1 = p.xp; + x2 = xp; + y1 = p.yp; + y2 = yp; + } + else + { + x1 = xp; + x2 = p.xp; + y1 = yp; + y2 = p.yp; + } + int dx = x2 - x1; + int dy = y2 - y1; + int e, i = 0; + Pt2i *pts; + + if (dy > 0) + { + // Octant 1 + if (dx >= dy) + { + *n = dx + 1; + pts = new Pt2i[dx + 1]; + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + pts[i++] = Pt2i (x1, y1); + x1 ++; + e -= dy; + if (e < 0) + { + y1 ++; + e += dx; + } + } + pts[i] = Pt2i (x1, y1); + } + + // Octant 2 + else + { + *n = dy + 1; + pts = new Pt2i[dy + 1]; + e = dy; + dx *= 2; + dy *= 2; + while (y1 < y2) + { + pts[i++] = Pt2i (x1, y1); + y1 ++; + e -= dx; + if (e < 0) + { + x1 ++; + e += dy; + } + } + pts[i] = Pt2i (x1, y1); + } + } + + else + { + // Octant 8 + if (dx >= -dy) + { + *n = 1 + dx; + pts = new Pt2i[dx + 1]; + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + pts[i++] = Pt2i (x1, y1); + x1 ++; + e += dy; + if (e < 0) + { + y1 --; + e += dx; + } + } + pts[i] = Pt2i (x1, y1); + } + + // Octant 7 + else + { + *n = 1 - dy; + pts = new Pt2i[1 - dy]; + e = dy; + dx *= 2; + dy *= 2; + while (y1 > y2) + { + pts[i++] = Pt2i (x1, y1); + y1 --; + e += dx; + if (e > 0) + { + x1 ++; + e += dy; + } + } + pts[i] = Pt2i (x1, y1); + } + } + return (pts); +} + + +void Pt2i::draw (vector<Pt2i> &line, Pt2i p) const +{ + int x1, y1, x2, y2; + if (xp > p.xp) + { + x1 = p.xp; + x2 = xp; + y1 = p.yp; + y2 = yp; + } + else + { + x1 = xp; + x2 = p.xp; + y1 = yp; + y2 = p.yp; + } + int dx = x2 - x1; + int dy = y2 - y1; + int e; + + if (dy > 0) + { + // Octant 1 + if (dx >= dy) + { + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + line.push_back (Pt2i (x1, y1)); + x1 ++; + e -= dy; + if (e < 0) + { + y1 ++; + e += dx; + } + } + line.push_back (Pt2i (x1, y1)); + } + + // Octant 2 + else + { + e = dy; + dx *= 2; + dy *= 2; + while (y1 < y2) + { + line.push_back (Pt2i (x1, y1)); + y1 ++; + e -= dx; + if (e < 0) + { + x1 ++; + e += dy; + } + } + line.push_back (Pt2i (x1, y1)); + } + } + + else + { + // Octant 8 + if (dx >= -dy) + { + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + line.push_back (Pt2i (x1, y1)); + x1 ++; + e += dy; + if (e < 0) + { + y1 --; + e += dx; + } + } + line.push_back (Pt2i (x1, y1)); + } + + // Octant 7 + else + { + e = dy; + dx *= 2; + dy *= 2; + while (y1 > y2) + { + line.push_back (Pt2i (x1, y1)); + y1 --; + e += dx; + if (e > 0) + { + x1 ++; + e += dy; + } + } + line.push_back (Pt2i (x1, y1)); + } + } +} + + +Pt2i *Pt2i::pathTo (Pt2i p, int *n) const +{ + int x1, y1, x2, y2, delta; + if (xp > p.xp) + { + x1 = p.xp; + x2 = xp; + y1 = p.yp; + y2 = yp; + delta = -1; + } + else + { + x1 = xp; + x2 = p.xp; + y1 = yp; + y2 = p.yp; + delta = 1; + } + int dx = x2 - x1; + int dy = y2 - y1; + int e, i = 0; + Pt2i *pts; + + if (dy > 0) + { + // Octant 1 + if (dx >= dy) + { + *n = dx; + pts = new Pt2i[dx]; + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + x1 ++; + e -= dy; + if (e < 0) + { + y1 ++; + e += dx; + pts[i++] = Pt2i (delta, delta); + } + else pts[i++] = Pt2i (delta, 0); + } + } + + // Octant 2 + else + { + *n = dy; + pts = new Pt2i[dy]; + e = dy; + dx *= 2; + dy *= 2; + while (y1 < y2) + { + y1 ++; + e -= dx; + if (e < 0) + { + x1 ++; + e += dy; + pts[i++] = Pt2i (delta, delta); + } + else pts[i++] = Pt2i (0, delta); + } + } + } + + else + { + // Octant 8 + if (dx >= -dy) + { + *n = dx; + pts = new Pt2i[dx]; + e = dx; + dx *= 2; + dy *= 2; + while (x1 < x2) + { + x1 ++; + e += dy; + if (e < 0) + { + y1 --; + e += dx; + pts[i++] = Pt2i (delta, -delta); + } + else pts[i++] = Pt2i (delta, 0); + } + } + // Octant 7 + else + { + *n = dy; + pts = new Pt2i[-dy]; + e = dy; + dx *= 2; + dy *= 2; + while (y1 > y2) + { + y1 --; + e += dx; + if (e > 0) + { + x1 ++; + e += dy; + pts[i++] = Pt2i (delta, -delta); + } + else pts[i++] = Pt2i (0, -delta); + } + } + } + return (pts); +} + + +bool *Pt2i::stepsTo (Pt2i p, int *n) const +{ + int x2 = (xp > p.xp) ? xp - p.xp : p.xp - xp; + int y2 = (yp > p.yp) ? yp - p.yp : p.yp - yp; + int dx = x2, dy = y2; + if (y2 > x2) + { + dx = y2; + dy = x2; + x2 = y2; + } + int e, x = 0, i = 0; + *n = x2; + bool *paliers = new bool[x2]; + + e = dx; + dx *= 2; + dy *= 2; + + while (x < x2) + { + x ++; + e -= dy; + if (e < 0) + { + e += dx; + paliers[i++] = true; + } + else paliers[i++] = false; + } + return (paliers); +} + + +vector<Pt2i> Pt2i::drawOrtho (const Pt2i p2, int offset) const +{ + vector<Pt2i> pts; + + int x1 = xp; + int y1 = yp; + int x2 = p2.x (); + int y2 = p2.y (); + int dx = x2 - x1; + int dy = y2 - y1; + int e; + + int num = dx * dy; + if (num < 0) num = - num; + int den = dx * dx + dy * dy; + int nabs = (offset < 0 ? -offset : offset); + int steps = (offset * num) / den; + if ((nabs * num) % den >= den / 2) steps += (offset < 0 ? -1 : 1); + int floors; + + if (dx > 0 && dy > 0) // Quadrant 1 + { + if (dx >= dy) // Octant 1 + { + e = dx; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (steps * dy - e) / dx; + else floors = (e - 1 + steps * dy) / dx; + e += floors * dx - steps * dy; + x1 += steps; + y1 -= offset - floors; + x2 += steps; + // y2 -= offset - floors; // inutile + while (x1 < x2) + { + pts.push_back (Pt2i (x1, y1)); + x1 ++; + e = e - dy; + if (e < 0) + { + y1 ++; + e = e + dx; + } + } + pts.push_back (Pt2i (x1, y1)); + } + else if (dx < dy) // Octant 2 + { + e = dy; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (1 - e + steps * dx) / dy; + else floors = (e + steps * dx) / dy; + e -= floors * dy - steps * dx; + x1 += offset - floors; + y1 -= steps; + // x2 += offset - floors; // inutile + y2 -= steps; // inutile + while (y1 < y2) + { + pts.push_back (Pt2i (x1, y1)); + y1 ++; + e -= dx; + if (e < 0) + { + x1 ++; + e += dy; + } + } + pts.push_back (Pt2i (x1, y1)); + } + } + + else if (dx > 0 && dy < 0) // Quadrant 4 + { + if (dx >= -dy) // Octant 8 + { + e = dx; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (e - 1 + steps * dy) / dx; + else floors = (steps * dy - e) / dx; // < 0 + e += floors * dx - steps * dy; + x1 -= steps; + y1 -= offset + floors; + x2 -= steps; + // y2 -= offset + floors; // inutile + while (x1 < x2) + { + pts.push_back (Pt2i (x1, y1)); + x1 ++; + e += dy; + if (e < 0) + { + y1 --; + e += dx; + } + } + pts.push_back (Pt2i (x1, y1)); + } + else // Octant 7 + { + e = dy; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = - (e + steps * dx) / dy; + else floors = (e + 1 - steps * dx) / dy; + e += floors * dy + steps * dx; + x1 -= offset - floors; + y1 -= steps; + // x2 -= offset - floors; // inutile + y2 -= steps; + while (y1 > y2) + { + pts.push_back (Pt2i (x1, y1)); + y1 --; + e += dx; + if (e > 0) + { + x1 ++; + e += dy; + } + } + pts.push_back (Pt2i (x1, y1)); + } + } + else if (dy == 0 && dx > 0) + while (x1 <= x2) pts.push_back (Pt2i (x1++, y1 - offset)); + + if (dx < 0 && dy > 0) // Quadrant 2 + { + if (-dx >= dy) // Octant 4 + { + e = dx; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = - (steps * dy + e) / dx; + else floors = (e + 1 - steps * dy) / dx; // > 0 + e -= steps * dy + floors * dx; + x1 += steps; + y1 += offset - floors; + x2 += steps; + // y2 += offset - floors; // inutile + while (x1 > x2) + { + pts.push_back (Pt2i (x1, y1)); + x1 --; + e += dy; + if (e >= 0) + { + y1 ++; + e += dx; + } + } + pts.push_back (Pt2i (x1, y1)); + } + else if (-dx < dy) // Octant 3 + { + e = dy; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (1 - e - steps * dx) / dy; + else floors = (e - steps * dx) / dy; + e += floors * dy + steps * dx; + x1 += offset - floors; + y1 += steps; + // x2 += offset - floors; // inutile + y2 += steps; + while (y1 < y2) + { + pts.push_back (Pt2i (x1, y1)); + y1 ++; + e += dx; + if (e <= 0) + { + x1 --; + e += dy; + } + } + pts.push_back (Pt2i (x1, y1)); + } + } + + else if (dx < 0 && dy < 0) // Quadrant 3 + { + if (dx <= dy) // Octant 5 + { + e = dx; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (steps * dy - e - 1) / dx; + else floors = (steps * dy + e) / dx; + e += floors * dx - steps * dy; + x1 -= steps; + y1 += offset - floors; + x2 -= steps; + // y2 += offset - floors; // inutile + while (x1 > x2) + { + pts.push_back (Pt2i (x1, y1)); + x1 --; + e -= dy; + if (e >= 0) + { + y1 --; + e += dx; + } + } + pts.push_back (Pt2i (x1, y1)); + } + else // Octant 6 + { + e = dy; + dx = dx * 2; + dy = dy * 2; + if (offset < 0) floors = (e - steps * dx) / dy; // > 0 + else floors = - (steps * dx + e + 1) / dy; + e += floors * dy + steps * dx; + x1 -= offset + floors; + y1 += steps; + y2 += steps; + // x2 -= offset - floors; // calcul inutile + while (y1 > y2) + { + pts.push_back (Pt2i (x1, y1)); + y1 --; + e -= dx; + if (e >= 0) + { + x1 --; + e += dy; + } + } + pts.push_back (Pt2i (x1, y1)); + } + } + else if (dy == 0 && dx < 0) + while (x1 >= x2) pts.push_back (Pt2i (x1--, y1 + offset)); + else if (dx == 0 && dy > 0) + while (y1 <= y2) pts.push_back (Pt2i (x1 + offset, y1++)); + else if (dx == 0 && dy < 0) + while(y1 >= y2) pts.push_back (Pt2i (x1 - offset, y1--)); + return pts; +} diff --git a/Code/Seg/ImageTools/pt2i.h b/Code/Seg/ImageTools/pt2i.h new file mode 100755 index 0000000000000000000000000000000000000000..a48b5b1d3bef9b3cbcab390b85c67e635eb514af --- /dev/null +++ b/Code/Seg/ImageTools/pt2i.h @@ -0,0 +1,247 @@ +#ifndef PT2I_H +#define PT2I_H + +#include "vr2i.h" +#include "absrat.h" +#include <vector> + +using namespace std; + + + +/** + * @class Pt2i pt2i.h + * \brief Point in the digital plane. + * \author {P. Even} + */ +class Pt2i +{ + +public: + + /** + * @fn Pt2i () + * \brief Creates a point at origin. + */ + Pt2i (); + + /** + * @fn Pt2i (int x, int y) + * \brief Creates a point using coordinates. + * @param x X coordinate. + * @param y Y coordinate. + */ + Pt2i (int x, int y); + + /** + * @fn Pt2i (Pt2i p) + * \brief Creates a copy of a given point. + * @param p Original. + */ + Pt2i (const Pt2i &p); + + /** + * @fn ~Pt2i () + * \brief Deletes the point. + */ + ~Pt2i () { } + + /** + * @fn int x () + * \brief Returns the point abscissae. + */ + inline int x () const + { + return xp; + } + + /** + * @fn int y () + * \brief Returns the point ordinate. + */ + inline int y () const + { + return yp; + } + + /** + * @fn int get (int n) + * \brief Returns the nth coordinate. + * @param n Coordinate index. + */ + inline int get (int n) const + { + return (n ? yp : xp); + } + + /** + * @fn void set (int x, int y) + * \brief Sets the point coordinates. + * @param x New abscissae. + * @param y New ordinate. + */ + inline void set (int x, int y) + { + xp = x; + yp = y; + } + + /** + * @fn void set (int x, int y) + * \brief Sets the point coordinates. + * @param point to recopy. + */ + inline void set (const Pt2i &p) + { + xp = p.xp; + yp = p.yp; + } + + /** + * @fn bool equals (Pt2i p) + * \brief Checks equivalence to the given point. + * @param p the given point. + */ + inline bool equals (Pt2i p) const + { + return (p.xp == xp && p.yp == yp); + } + + /** + * @fn int manhattan (Pt2i p) + * \brief Returns the manhattan distance to the given point. + * @param p the given point. + */ + inline int manhattan (Pt2i p) const + { + return (((p.xp > xp) ? p.xp - xp : xp - p.xp) + + ((p.yp > yp) ? p.yp - yp : yp - p.yp)); + } + + /** + * @fn int isConnectedTo (Pt2i p) + * \brief Checks whether the point is connected (chessboard) to the given one. + * @param p the given point. + */ + inline bool isConnectedTo (Pt2i p) const + { + return ((p.xp - xp <= 1) && (xp - p.xp <= 1) + && (p.yp - yp <= 1) && (yp - p.yp <= 1)); + } + + /** + * @fn bool colinearTo (const Point2D &p1, const Point2D &p2) + * Checks whether the point is colinear to tho other point. + * @param p1 First distinct point. + * @param p2 Second distinct point. + */ + inline bool colinearTo (const Pt2i &p1, const Pt2i &p2) const + { + return ((p1.xp - xp) * (p2.yp - yp) == (p2.xp - xp) * (p1.yp - yp)); + } + + /** + * @fn bool toLeft (const Point2D &p1, const Point2D &p2) + * Checks whether the point is to the left of the segment (p1p2). + * @param p1 Segment start point. + * @param p2 Segment end point. + */ + inline bool toLeft (const Pt2i &p1, const Pt2i &p2) const + { + return ((p1.xp - xp) * (p2.yp - yp) > (p2.xp - xp) * (p1.yp - yp)); + } + + /** + * @fn bool toLeftOrOn (const Point2D &p1, const Point2D &p2) + * Checks whether the point is to the left or on the segment (p1p2). + * @param p1 Segment start point. + * @param p2 Segment end point. + */ + inline bool toLeftOrOn (const Pt2i &p1, const Pt2i &p2) const + { + return ((p1.xp - xp) * (p2.yp - yp) >= (p2.xp - xp) * (p1.yp - yp)); + } + + /** + * @fn AbsRat triangleRationalHeight (const Pt2i &p1, const Pt2i &p2) + * \brief Returns the X/Y height of the triangle with two other points. + * @param p1 First distinct point. + * @param p2 Second distinct point. + */ + AbsRat triangleRationalHeight (const Pt2i &p1, const Pt2i &p2) const; + + /** + * @fn AbsRat triangleXRationalHeight (const Pt2i &p1, const Pt2i &p2) + * \brief Returns the vertical height of the triangle with two other points. + * @param p1 First distinct point. + * @param p2 Second distinct point. + */ + AbsRat triangleXRationalHeight (const Pt2i &p1, const Pt2i &p2) const; + + /** + * @fn AbsRat triangleYRationalHeight (const Pt2i &p1, const Pt2i &p2) + * \brief Returns the horizontal height of the triangle with two other points. + * @param p1 First distinct point. + * @param p2 Second distinct point. + */ + AbsRat triangleYRationalHeight (const Pt2i &p1, const Pt2i &p2) const; + + /** + * @fn Vr2i vectorTo (Pt2i p) + * \brief Returns the discrete vector to the given point. + * @param p the given point. + */ + Vr2i vectorTo (Pt2i p) const; + + /** + * @fn Pt2i *drawing (Pt2i p, int *n) + * \brief Returns the segment to the given point. + * @param p the given point. + * @param n size of the returned array. + */ + Pt2i *drawing (const Pt2i p, int *n) const; + + /** + * @fn Pt2i *drawing (Pt2i p, int *n) + * \brief Fills the provided vector with the segment to the given point. + * @param line Vector of points to fill in. + * @param p Distant point. + */ + void draw (vector<Pt2i> &line, Pt2i p) const; + + /** + * @fn Pt2i *pathTo (Pt2i p, int *n) + * \brief Returns the succession of local moves to reach the given point. + * A local move is the vector from a point to one of his neighbours. + * @param p the given point. + * @param n size of the returned array. + */ + Pt2i *pathTo (Pt2i p, int *n) const; + + /** + * @fn bool *stepsTo (Pt2i p, int *n) + * \brief Returns the location of the steps to reach the given point. + * @param p the given point. + * @param n size of the returned array. + */ + bool *stepsTo (Pt2i p, int *n) const; + + /** + * @fn vector<Pt2i> drawOrtho (const Pt2i p2, int offset) const + * \brief Returns the segment orthogonal to the segment to p2. + * @param p2 The distant point. + * @param offset The orthogonal offset. + */ + vector<Pt2i> drawOrtho (const Pt2i p2, int offset) const; + + +protected: + + /** Point abscissae. */ + int xp; + /** Point ordinate. */ + int yp; + +}; + +#endif diff --git a/Code/Seg/ImageTools/strucel.cpp b/Code/Seg/ImageTools/strucel.cpp new file mode 100755 index 0000000000000000000000000000000000000000..caade0b8f74bedd28f482e6ffffd913e17b41fb1 --- /dev/null +++ b/Code/Seg/ImageTools/strucel.cpp @@ -0,0 +1,158 @@ +#include <iostream> +#include "strucel.h" + +using namespace std; + + +const int Strucel::TYPE_PLUS_3X3 = 0; + + +Strucel::Strucel (int type) +{ + if (type == TYPE_PLUS_3X3) + { + width = 3; + height = 3; + size = 5; + pattern = new Vr2i[5]; + pattern[0] = Vr2i (0, 0); + pattern[1] = Vr2i (0, 1); + pattern[2] = Vr2i (-1, 0); + pattern[3] = Vr2i (1, 0); + pattern[4] = Vr2i (0, -1); + } + else + { + width = 1; + height = 1; + size = 1; + pattern = new Vr2i[1]; + pattern[0] = Vr2i (1, 1); + } +} + + +Strucel::~Strucel () +{ + delete pattern; +} + + +void Strucel::tophatGradient (int *out, int **in, int width, int height) +{ +cout << "TH IN" << endl; + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int min = -1; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + if (min == -1 || in[y][x] < min) min = in[y][x]; + } + out[j * width + i] = in[j][i] - min; + } +cout << "TH OUT" << endl; +} + + +void Strucel::tophatGradient (int *out, int *in, int width, int height) +{ + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int min = -1; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + if (min == -1 || in[y * width + x] < min) min = in[y * width + x]; + } + out[j * width + i] = in[j * width + i] - min; + } +} + + +void Strucel::blackhatGradient (int *out, int **in, int width, int height) +{ +cout << "BH IN" << endl; + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int max = 0; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + if (in[y][x] > max) max = in[y][x]; + } + out[j * width + i] = max - in[j][i]; + } +cout << "BH OUT" << endl; +} + + +void Strucel::blackhatGradient (int *out, int *in, int width, int height) +{ + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int max = 0; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + if (in[y * width + x] > max) max = in[y * width + x]; + } + out[j * width + i] = max - in[j * width + i]; + } +} + + +void Strucel::morphoGradient (int *out, int **in, int width, int height) +{ + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int max = 0; + int min = -1; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + { + if (min == -1 || in[y][x] < min) min = in[y][x]; + if (in[y][x] > max) max = in[y][x]; + } + } + out[j * width + i] = max - min; + } +} + + +void Strucel::morphoGradient (int *out, int *in, int width, int height) +{ + for (int j = 0; j < height; j++) + for (int i = 0; i < width; i++) + { + int max = 0; + int min = -1; + for (int k = 0; k < size; k++) + { + int x = i - pattern[k].x (); + int y = j - pattern[k].y (); + if (x >= 0 && x < width && y >= 0 && y < height) + { + if (min == -1 || in[y * width + x] < min) min = in[y * width + x]; + if (in[y * width + x] > max) max = in[y * width + x]; + } + } + out[j * width + i] = max - min; + } +} diff --git a/Code/Seg/ImageTools/strucel.h b/Code/Seg/ImageTools/strucel.h new file mode 100755 index 0000000000000000000000000000000000000000..9503cd28d8e1242eb70ad9fb7c28b59a79374025 --- /dev/null +++ b/Code/Seg/ImageTools/strucel.h @@ -0,0 +1,118 @@ +#ifndef STRUCEL_H +#define STRUCEL_H + +#include "vr2i.h" + +using namespace std; + + +/** + * @class Strucel strucel.h + * \brief Structuring element for morphological operations. + */ +class Strucel +{ + +public: + + /** 3x3 cross structuring element type. */ + static const int TYPE_PLUS_3X3; + + + /** + * \brief Creates a structural element. + * @param type Pattern type. + */ + Strucel (int type); + + /** + * \brief Deletes the structuring element. + */ + ~Strucel (); + + /** + * \brief Returns the pattern width. + */ + inline int getWidth () const + { + return width; + } + + /** + * \brief Returns the pattern height. + + */ + inline int getHeight () const + { + return height; + } + + /** + * \brief Calculates the top hat gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image bi-dimensional array. + * @param width Image width. + * @param height Image height. + */ + void tophatGradient (int *out, int **in, int width, int height); + + /** + * \brief Calculates the top hat gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image array. + * @param width Image width. + * @param height Image height. + */ + void tophatGradient (int *out, int *in, int width, int height); + + /** + * \brief Calculates the black hat gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image bi-dimensional array. + * @param width Image width. + * @param height Image height. + */ + void blackhatGradient (int *out, int **in, int width, int height); + + /** + * \brief Calculates the black hat gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image array. + * @param width Image width. + * @param height Image height. + */ + void blackhatGradient (int *out, int *in, int width, int height); + + /** + * \brief Calculates the morphological gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image bi-dimensional array. + * @param width Image width. + * @param height Image height. + */ + void morphoGradient (int *out, int **in, int width, int height); + + /** + * \brief Calculates the morphological gradient of an image. + * @param out Output gradient array (the memory should be allocated before). + * @param in Image array. + * @param width Image width. + * @param height Image height. + */ + void morphoGradient (int *out, int *in, int width, int height); + + +private: + + /** Width of the structuring element. */ + int width; + /** Height of the structuring element. */ + int height; + /** Size of the pattern. */ + int size; + /** Pattern of the structuring element as the list of occupied pixels. */ + Vr2i *pattern; + +}; + +#endif diff --git a/Code/Seg/ImageTools/vmap.cpp b/Code/Seg/ImageTools/vmap.cpp new file mode 100755 index 0000000000000000000000000000000000000000..3cd61824e5cf80663da62d8c3211fc2efc3e3a64 --- /dev/null +++ b/Code/Seg/ImageTools/vmap.cpp @@ -0,0 +1,486 @@ +// #include <iostream> +#include "vmap.h" +#include "math.h" + +using namespace std; + + +const int VMap::TYPE_SOBEL_3X3 = 0; +const int VMap::TYPE_SOBEL_5X5 = 1; +const int VMap::TYPE_TOP_HAT = 2; +const int VMap::TYPE_BLACK_HAT = 3; +const int VMap::TYPE_MORPHO = 4; + +const int VMap::NEAR_SQ_ANGLE = 80; // 80% (roughly 25 degrees) +const int VMap::LARGE_SQ_ANGLE = 25; // 25% (60 degrees) +const int VMap::DEFAULT_GRADIENT_THRESHOLD = 30; + + +VMap::VMap (int width, int height, int *data, int type) +{ + this->width = width; + this->height = height; + if (type == TYPE_SOBEL_5X5) buildSobel5x5Map (data); + else buildGradientMap (data); + imap = new int[width * height]; + if (type == TYPE_TOP_HAT) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.tophatGradient (imap, data, width, height); + } + else if (type == TYPE_BLACK_HAT) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.blackhatGradient (imap, data, width, height); + } + else if (type == TYPE_MORPHO) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.morphoGradient (imap, data, width, height); + } + else + for (int i = 0; i < width * height; i++) + imap[i] = (int) sqrt (map[i].norm2 ()); + mask = new bool[width * height]; + for (int i = 0; i < width * height; i++) mask[i] = false; + masking = false; + angleThreshold = NEAR_SQ_ANGLE; + orientedGradient = false; + formerOrientedGradient = false; +} + + +VMap::VMap (int width, int height, int **data, int type) +{ + this->width = width; + this->height = height; + if (type == TYPE_SOBEL_5X5) buildSobel5x5Map (data); + else buildGradientMap (data); + imap = new int[width * height]; + if (type == TYPE_TOP_HAT) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.tophatGradient (imap, data, width, height); + } + else if (type == TYPE_BLACK_HAT) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.blackhatGradient (imap, data, width, height); + } + else if (type == TYPE_MORPHO) + { + Strucel se (Strucel::TYPE_PLUS_3X3); + se.morphoGradient (imap, data, width, height); + } + else + for (int i = 0; i < width * height; i++) + imap[i] = sqrt (map[i].norm2 ()); + mask = new bool[width * height]; + for (int i = 0; i < width * height; i++) mask[i] = false; + masking = false; + angleThreshold = NEAR_SQ_ANGLE; + orientedGradient = false; + formerOrientedGradient = false; +} + + +VMap::~VMap () +{ + delete map; + delete imap; + delete mask; +} + + +void VMap::buildGradientMap (int *data) +{ + map = new Vr2i[width * height]; + Vr2i *gm = map; + + for (int j = 0; j < width; j++) + { + gm->set (0, 0); + gm++; + } + for (int i = 1; i < height - 1; i++) + { + gm->set (0, 0); + gm++; + for (int j = 1; j < width - 1; j++) + { + gm->set (data[(i - 1) * height + j + 1] + + 2 * data[i * height + j + 1] + + data[(i + 1) * height + j + 1] + - data[(i - 1) * height + j - 1] + - 2 * data[i * height + j - 1] + - data[(i + 1) * height + j - 1], + data[(i + 1) * height + j - 1] + + 2 * data[(i + 1) * height + j] + + data[(i + 1) * height + j + 1] + - data[(i - 1) * height + j - 1] + - 2 * data[(i - 1) * height + j] + - data[(i - 1) * height + j + 1]); + gm++; + } + gm->set (0, 0); + gm++; + } + for (int j = 0; j < width; j++) + { + gm->set (0, 0); + gm++; + } +} + + +void VMap::buildGradientMap (int **data) +{ + map = new Vr2i[width * height]; + Vr2i *gm = map; + + for (int j = 0; j < width; j++) + { + gm->set (0, 0); + gm++; + } + for (int i = 1; i < height - 1; i++) + { + gm->set (0, 0); + gm++; + for (int j = 1; j < width - 1; j++) + { + gm->set (data[i-1][j+1] + 2 * data[i][j+1] + data[i+1][j+1] + - data[i-1][j-1] - 2 * data[i][j-1] - data[i+1][j-1], + data[i+1][j-1] + 2 * data[i+1][j] + data[i+1][j+1] + - data[i-1][j-1] - 2 * data[i-1][j] - data[i-1][j+1]); + gm++; + } + gm->set (0, 0); + gm++; + } + for (int j = 0; j < width; j++) + { + gm->set (0, 0); + gm++; + } +} + + +void VMap::buildSobel5x5Map (int *data) +{ + map = new Vr2i[width * height]; + Vr2i *gm = map; + + for (int j = 0; j < 2 * width; j++) + { + gm->set (0, 0); + gm++; + } + for (int i = 2; i < height - 2; i++) + { + gm->set (0, 0); + gm++; + gm->set (0, 0); + gm++; + for (int j = 2; j < width - 2; j++) + { + gm->set (5 * data[(i - 2) * height + j + 2] + + 8 * data[(i - 1) * height + j + 2] + + 10 * data[i * height + j + 2] + + 8 * data[(i + 1) * height + j + 2] + + 5 * data[(i + 2) * height + j + 2] + + 4 * data[(i - 2) * height + j + 1] + + 10 * data[(i - 1) * height + j + 1] + + 20 * data[i * height + j + 1] + + 10 * data[(i + 1) * height + j + 1] + + 4 * data[(i + 2) * height + j + 1] + - 4 * data[(i - 2) * height + j - 1] + - 10 * data[(i - 1) * height + j - 1] + - 20 * data[i * height + j - 1] + - 10 * data[(i + 1) * height + j - 1] + - 4 * data[(i + 2) * height + j - 1] + - 5 * data[(i - 2) * height + j - 2] + - 8 * data[(i - 1) * height + j - 2] + - 10 * data[i * height + j - 2] + - 8 * data[(i + 1) * height + j - 2] + - 5 * data[(i + 2) * height + j - 2], + 5 * data[(i + 2) * height + j - 2] + + 8 * data[(i + 2) * height + j - 1] + + 10 * data[(i + 2) * height + j] + + 8 * data[(i + 2) * height + j + 1] + + 5 * data[(i + 2) * height + j + 2] + + 4 * data[(i + 1) * height + j - 2] + + 10 * data[(i + 1) * height + j - 1] + + 20 * data[(i + 1) * height + j] + + 10 * data[(i + 1) * height + j + 1] + + 4 * data[(i + 1) * height + j + 2] + - 4 * data[(i - 1) * height + j - 2] + - 10 * data[(i - 1) * height + j - 1] + - 20 * data[(i - 1) * height + j] + - 10 * data[(i - 1) * height + j + 1] + - 4 * data[(i - 1) * height + j + 2] + - 5 * data[(i - 2) * height + j - 2] + - 8 * data[(i - 2) * height + j - 1] + - 10 * data[(i - 2) * height + j] + - 8 * data[(i - 2) * height + j + 1] + - 5 * data[(i - 2) * height + j + 2]); + gm++; + } + gm->set (0, 0); + gm++; + gm->set (0, 0); + gm++; + } + for (int j = 0; j < 2 * width; j++) + { + gm->set (0, 0); + gm++; + } +} + + +void VMap::buildSobel5x5Map (int **data) +{ + map = new Vr2i[width * height]; + Vr2i *gm = map; + + for (int j = 0; j < 2 * width; j++) + { + gm->set (0, 0); + gm++; + } + for (int i = 2; i < height - 2; i++) + { + gm->set (0, 0); + gm++; + gm->set (0, 0); + gm++; + for (int j = 2; j < width - 2; j++) + { + gm->set ( + 5 * data[i-2][j+2] + 8 * data[i-1][j+2] + 10 * data[i][j+2] + + 8 * data[i+1][j+2] + 5 * data[i+2][j+2] + + 4 * data[i-2][j+1] + 10 * data[i-1][j+1] + 20 * data[i][j+1] + + 10 * data[i+1][j+1] + 4 * data[i+2][j+1] + - 4 * data[i-2][j-1] - 10 * data[i-1][j-1] - 20 * data[i][j-1] + - 10 * data[i+1][j-1] - 4 * data[i+2][j-1] + - 5 * data[i-2][j-2] - 8 * data[i-1][j-2] - 10 * data[i][j-2] + - 8 * data[i+1][j-2] - 5 * data[i+2][j-2], + 5 * data[i+2][j-2] + 8 * data[i+2][j-1] + 10 * data[i+2][j] + + 8 * data[i+2][j+1] + 5 * data[i+2][j+2] + + 4 * data[i+1][j-2] + 10 * data[i+1][j-1] + 20 * data[i+1][j] + + 10 * data[i+1][j+1] + 4 * data[i+1][j+2] + - 4 * data[i-1][j-2] - 10 * data[i-1][j-1] - 20 * data[i-1][j] + - 10 * data[i-1][j+1] - 4 * data[i-1][j+2] + - 5 * data[i-2][j-2] - 8 * data[i-2][j-1] - 10 * data[i-2][j] + - 8 * data[i-2][j+1] - 5 * data[i-2][j+2]); + gm++; + } + gm->set (0, 0); + gm++; + gm->set (0, 0); + gm++; + } + for (int j = 0; j < 2 * width; j++) + { + gm->set (0, 0); + gm++; + } +} + + +int VMap::sqNorm (int i, int j) const +{ + return (map[j * width + i].norm2 ()); +} + + +int VMap::sqNorm (Pt2i p) const +{ + return (map[p.y () * width + p.x ()].norm2 ()); +} + + +int VMap::largestIn (const vector<Pt2i> &pix) const +{ + if (pix.empty ()) return (-1); + + int imax = -1; + vector<Pt2i>::const_iterator pt = pix.begin (); + int gmax = imap[pt->y() * width + pt->x()]; + if (gmax < DEFAULT_GRADIENT_THRESHOLD) gmax = DEFAULT_GRADIENT_THRESHOLD; + + int i = 0; + while (pt != pix.end ()) + { + int g = imap[pt->y() * width + pt->x()]; + if (g > gmax) + { + gmax = g; + imax = i; + } + pt ++; + i ++; + } + return (imax == i - 1 ? -1 : imax); +} + + +int VMap::keepFreeElementsIn (const vector<Pt2i> &pix, int n, int *ind) const +{ + int i = 0; + while (i < n) + { + if (mask[pix[ind[i]].y () * width + pix[ind[i]].x ()]) ind[i] = ind[--n]; + else i++; + } + return (n); +} + + +int VMap::keepOrientedElementsIn (const vector<Pt2i> &pix, + const Vr2i &ref, int n, int *ind) const +{ + int vx = ref.x (); + int vy = ref.y (); + int i = 0; + while (i < n) + { + Vr2i gr = map[pix[ind[i]].y () * width + pix[ind[i]].x ()]; + if (vx * gr.x () + vy * gr.y () <= 0) ind[i] = ind[--n]; + else i++; + } + return (n); +} + + +int VMap::keepDirectedElementsIn (const vector<Pt2i> &pix, + const Vr2i &ref, int n, int *ind) const +{ + int vx = ref.x (); + int vy = ref.y (); + long vn2 = vx * vx + vy * vy; + + int i = 0; + while (i < n) + { + Pt2i p = pix.at (ind[i]); + Vr2i gr = map[p.y () * width + p.x ()]; + long gx = gr.x (); + long gy = gr.y (); + if ((vx * vx * gx * gx + vy * vy * gy * gy + 2 * vx * vy * gx * gy) * 100 + < vn2 * (gx * gx + gy * gy) * angleThreshold) + ind[i] = ind[--n]; + else i++; + } + return (n); +} + + +int VMap::localMax (int *lmax, const vector<Pt2i> &pix, const Vr2i &gref) const +{ + // Builds the gradient norm signal + int n = (int) pix.size (); + int *gn = new int[n]; + int i = 0; + vector<Pt2i>::const_iterator it = pix.begin (); + while (it != pix.end ()) gn[i++] = cmpNorm (*it++); + + // Gets the local maxima + int count = searchLocalMax (lmax, n, gn); + + // Prunes the already selected candidates + if (masking) + count = keepFreeElementsIn (pix, count, lmax); + + // Prunes the candidates with opposite gradient + if (orientedGradient) + count = keepOrientedElementsIn (pix, gref, count, lmax); + + // Prunes the candidates wrongly oriented + count = keepDirectedElementsIn (pix, gref, count, lmax); + + // Sorts candidates by gradient magnitude + sortMax (lmax, count, gn); + + delete gn; + return count; +} + + +int VMap::searchLocalMax (int *lmax, int n, int *in) const +{ + int offset = 0; + int count = 0; + bool up = true; + + // Gets the first distinct value from start + while (offset < n - 1 && in[offset] == in[0]) + { + if (in[offset] - in[offset + 1] < 0) + { + up = true; + break; + } + if (in[offset] - in[offset + 1] > 0) + { + up = false; + break; + } + offset++; + } + + for(int i = offset; i < n - 1; i++) + { + if (up) + { + if ((in[i + 1] - in[i]) < 0) + { + up = false; + int k = i; + while (in[k - 1] == in[i]) k--; + lmax[count++] = k + (i - k) / 2; + } + } + else + if (in[i + 1] - in[i] > 0) up = true; + } + return count; +} + + +void VMap::sortMax (int *lmax, int n, int *val) const +{ + for (int i = 1; i < n; i ++) + { + int j = i, tmp = lmax[i]; + bool on = true; + while (on && j > 0) + { + if (val[tmp] > val[lmax[j-1]]) + { + lmax[j] = lmax[j-1]; + j --; + } + else on = false; + } + lmax[j] = tmp; + } +} + + +void VMap::clearMask () +{ + for (int i = 0; i < width * height; i++) mask[i] = false; +} + + +void VMap::setMask (const vector<Pt2i> &pts) +{ + vector<Pt2i>::const_iterator it = pts.begin (); + while (it != pts.end ()) + { + Pt2i pt = *it++; + mask[pt.y () * width + pt.x ()] = true; + } +} diff --git a/Code/Seg/ImageTools/vmap.h b/Code/Seg/ImageTools/vmap.h new file mode 100755 index 0000000000000000000000000000000000000000..c846596367e068dedca685425f4f079f94017b69 --- /dev/null +++ b/Code/Seg/ImageTools/vmap.h @@ -0,0 +1,303 @@ +#ifndef VMAP_H +#define VMAP_H + +#include "pt2i.h" +#include "strucel.h" + +using namespace std; + + +/** + * @class VMap vmap.h + * \brief Vector map. + * \author {P. Even} + */ +class VMap +{ + +public: + + /** Gradient extraction method : Sobel with 3x3 kernel. */ + static const int TYPE_SOBEL_3X3; + /** Gradient extraction method : Sobel with 5x5 kernel. */ + static const int TYPE_SOBEL_5X5; + /** Gradient extraction method : Morphological top hat (I - E(I)). */ + static const int TYPE_TOP_HAT; + /** Gradient extraction method : Morphological black hat (D(I) - I). */ + static const int TYPE_BLACK_HAT; + /** Gradient extraction method : Morphological gradient (D(I) - E(I)). */ + static const int TYPE_MORPHO; + + + /** + * \brief Creates a gradient map from scalar data. + * @param width Map width. + * @param height Map height. + * @param data Scalar data array. + * @param type Gradient extraction method (default is Soble with 3x3 kernel). + */ + VMap (int width, int height, int *data, int type = 0); + + /** + * \brief Creates a gradient map from scalar data. + * @param width Map width. + * @param height Map height. + * @param data Scalar data bi-dimensional array. + */ + VMap (int width, int height, int **data, int type = 0); + + /** + * \brief Deletes the vector map. + */ + ~VMap (); + + /** + * \brief Returns the map width. + */ + inline int getWidth () const + { + return width; + } + + /** + * \brief Returns the map height. + */ + inline int getHeight () const + { + return height; + } + + /** + * \brief Returns the maximal value between map width or height. + */ + inline int getHeightWidthMax () const + { + return (height > width ? height : width); + } + + /** + * \brief Checks whether the given point lies within the map bounds. + * @param i Column number. + * @param j Line number. + */ + inline bool contains (int i, int j) + { + return (i >= 0 && i < width && j >= 0 && j < height); + } + + /** + * \brief Returns the vector at point (i,j). + * @param i Column number. + * @param j Line number. + */ + Vr2i getValue (int i, int j) const + { + return (map[j * width + i]); + } + + /** + * \brief Returns the vector at given point. + * @param p Point position. + */ + Vr2i getValue (Pt2i p) const + { + return (map[p.y () * width + p.x ()]); + } + + /** + * \brief Returns the squared norm of the vector magnitude at point (i,j). + * @param i Column number. + * @param j Line number. + */ + int sqNorm (int i, int j) const; + + /** + * \brief Returns the squared norm of the vector magnitude at given point. + * @param p Point position. + */ + int sqNorm (Pt2i p) const; + + /** + * \brief Returns the comparable norm of the vector magnitude at point (i,j). + * @param i Column number. + * @param j Line number. + */ + inline int cmpNorm (int i, int j) const { return (imap[j * width + i]); } + + /** + * \brief Returns the comparable norm of the vector magnitude at given point. + * @param p Point position. + */ + inline int cmpNorm (Pt2i p) const { return (imap[p.y () * width + p.x ()]); } + + /** + * \brief Returns the index of the largest vector at given positions. + * First and last points of the list are not accepted. + * A gradient minimal threshold is set for the test. + * Returns -1 if no max is found. + * @param pix List of points. + */ + int largestIn (const vector<Pt2i> &pix) const; + + /** + * Keeps elements that are not already selected (in the mask array). + * Returns the number of remaining elements in the selection. + * @param pix Input array of scanned points. + * @param n Initial size of the selection of points. + * @param ind Selection of points. + */ + int keepFreeElementsIn (const vector<Pt2i> &pix, int n, int *ind) const; + + /** + * Keeps elements that are oriented along a reference vector + * in a selection of points. + * Returns the number of remaining elements in the selection. + * @param pix Input array of scanned points. + * @param ref The reference vector. + * @param n Initial size of the selection of points. + * @param ind Selection of points. + */ + int keepOrientedElementsIn (const vector<Pt2i> &pix, + const Vr2i &ref, int n, int *ind) const; + + /** + * Keeps elements with gradient value near a reference vector + * in a selection of points. + * Returns the number of remaining elements in the selection. + * @param pix Input array of scanned points. + * @param ref The reference vector. + * @param n Initial size of the selection of points. + * @param ind Selection of points. + */ + int keepDirectedElementsIn (const vector<Pt2i> &pix, + const Vr2i &ref, int n, int *ind) const; + + /** + * \brief Gets filtered and sorted local gradient maxima. + * Local maxima are filtered according to the gradient direction and sorted. + * Returns the count of found gradient maxima. + * @param lmax Local max index array. + * @param pix Provided points. + * @param gref Gradient vector reference. + */ + int localMax (int *lmax, const vector<Pt2i> &pix, const Vr2i &gref) const; + + /** + * \brief Switches the direction constraint for local maxima selection. + */ + inline void switchOrientationConstraint () { + orientedGradient = ! orientedGradient; } + + /** + * \brief Temporarily releases the direction constraint for large angle tests. + */ + inline void releaseOrientationConstraint () { + formerOrientedGradient = orientedGradient; + orientedGradient = false; + angleThreshold = LARGE_SQ_ANGLE; + } + + /** + * \brief Restores the direction constraint status for fine angle tests. + */ + inline void restoreOrientationConstraint () { + orientedGradient = formerOrientedGradient; + angleThreshold = NEAR_SQ_ANGLE; + } + + /** + * \brief Clears the occupancy mask. + */ + void clearMask (); + + /** + * \brief Adds positions to the occupancy mask. + * @param pts Vector of points. + */ + void setMask (const vector<Pt2i> &pts); + + /** + * \brief Sets mask activation on or off + * @param status Required activation status. + */ + inline void setMasking (bool status) { masking = status; } + + +private: + + /** Default value for near angular deviation tests. */ + static const int NEAR_SQ_ANGLE; + /** Default value for large angular deviation tests. */ + static const int LARGE_SQ_ANGLE; + /** Effective value for the angular deviation test. */ + int angleThreshold; + /** Default threshold value for the gradient selection. */ + static const int DEFAULT_GRADIENT_THRESHOLD; + + /** Image width. */ + int width; + /** Image height. */ + int height; + /** Vector map. */ + Vr2i *map; + /** Intensity map (squarred norm or morphologicalgradient). */ + int *imap; + + /** Occupancy mask. */ + bool *mask; + /** Flag indicating whether the occupancy mask is in use. */ + bool masking; + /** Direction constraint for local gradient maxima. */ + bool orientedGradient; + /** Registred direction constraint for local gradient maxima. */ + bool formerOrientedGradient; + + + /** + * \brief Builds the vector map as a gradient map from provided data. + * Uses a Sobel 3x3 kernel. + * @param data Initial scalar data. + */ + void buildGradientMap (int *data); + + /** + * \brief Builds the vector map as a gradient map from provided data. + * Uses a Sobel 3x3 kernel. + * @param data Initial bi-dimensional scalar data. + */ + void buildGradientMap (int **data); + + /** + * \brief Builds the vector map as a gradient map from provided data. + * Uses a Sobel 5x5 kernel. + * @param data Initial scalar data. + */ + void buildSobel5x5Map (int *data); + + /** + * \brief Builds the vector map as a gradient map from provided data. + * Uses a Sobel 5x5 kernel. + * @param data Initial bi-dimensional scalar data. + */ + void buildSobel5x5Map (int **data); + + /** + * \brief Searches local gradient maxima values. + * Returns the count of local maxima found. + * @param lmax Local max index array. + * @param n Count of input values. + * @param in Array of input values. + */ + int searchLocalMax (int *lmax, int n, int *in) const; + + /** + * \brief Sorts the candidates array by highest magnitude. + * @param lmax Local max index array. + * @param n Size of index array. + * @param val Input values. + */ + void sortMax (int *lmax, int n, int *val) const; + +}; + +#endif diff --git a/Code/Seg/ImageTools/vr2i.cpp b/Code/Seg/ImageTools/vr2i.cpp new file mode 100755 index 0000000000000000000000000000000000000000..5f0ab4d8eb15a7bf94a377bba71a1b4a7dca688a --- /dev/null +++ b/Code/Seg/ImageTools/vr2i.cpp @@ -0,0 +1,62 @@ +#include "vr2i.h" + + +Vr2i::Vr2i () +{ + xv = 1; + yv = 0; +} + + +Vr2i::Vr2i (int x, int y) +{ + xv = x; + yv = y; +} + + +Vr2i::Vr2i (const Vr2i &v) +{ + xv = v.xv; + yv = v.yv; +} + + +Vr2i Vr2i::orthog () const +{ + return (Vr2i (-yv, xv)); +} + + +bool *Vr2i::steps (int *n) const +{ + int x2 = (xv > 0 ? xv : - xv); + int y2 = (yv > 0 ? yv : - yv); + int dx = x2, dy = y2; + if (y2 > x2) + { + dx = y2; + dy = x2; + x2 = y2; + } + int e, x = 0, i = 0; + *n = x2; + bool *paliers = new bool[x2]; + + e = dx; + dx *= 2; + dy *= 2; + + while (x < x2) + { + x ++; + e -= dy; + if (e < 0) + { + e += dx; + paliers[i++] = true; + } + else paliers[i++] = false; + } + return (paliers); +} diff --git a/Code/Seg/ImageTools/vr2i.h b/Code/Seg/ImageTools/vr2i.h new file mode 100755 index 0000000000000000000000000000000000000000..95b34edbd00c6ff18b439c758f5ec32341e96bf6 --- /dev/null +++ b/Code/Seg/ImageTools/vr2i.h @@ -0,0 +1,160 @@ +#ifndef VR2I_H +#define VR2I_H + +using namespace std; + + +/** + * @class Vr2i vr2i.h + * \brief Vector in the discrete plane. + * \author {P. Even} + */ +class Vr2i +{ + +public: + + /** + * @fn Vr2i () + * \brief Creates a unit vector on X axis. + */ + Vr2i (); + + /** + * @fn Vr2i (int x, int y) + * \brief Creates a vector using coordinates. + * @param x abscissae. + * @param y ordinate. + */ + Vr2i (int x, int y); + + /** + * @fn Vr2i (Vr2i p) + * \brief Creates a copy of a given vector. + * @param v original. + */ + Vr2i (const Vr2i &v); + + /** + * @fn int x () + * \brief Returns the vector abscissae. + */ + inline int x () const + { + return xv; + } + + /** + * @fn int y () + * \brief Returns the vector ordinate. + */ + inline int y () const + { + return yv; + } + + /** + * @fn void set (int x, int y) + * \brief Sets the vector coordinates. + * @param x new abscissae. + * @param y new ordinate. + */ + inline void set (int x, int y) + { + xv = x; + yv = y; + } + + /** + * @fn int norm2 () + * \brief Returns the squared norm of the vector. + * If intensity value holds on a byte, gradient holds on a short + * and gradient squared norm holds on a int. + */ + inline int norm2 () const + { + return (xv * xv + yv * yv); + } + + /** + * @fn int scalarProduct (Vr2i vec) + * \brief Returns the scalar product with the given vector. + * If intensity value holds on a byte, scalar product (SP) holds on a short + * and squared SP holds on a int. + */ + inline int scalarProduct (Vr2i vec) const + { + return (xv * vec.xv + yv * vec.yv); + } + + /** + * @fn int squaredScalarProduct (Vr2i vec) + * \brief Returns the sqaured scalar product with the given vector. + */ + inline int squaredScalarProduct (Vr2i vec) const + { + return ((xv * vec.xv + yv * vec.yv) + * (xv * vec.xv + yv * vec.yv)); + } + + /** + * @fn bool equals (Vr2i p) + * \brief Checks equivalence to the given vector. + * @param v the given vector. + */ + inline bool equals (Vr2i v) const + { + return (v.xv == xv && v.yv == yv); + } + + /** + * @fn int manhattan () + * \brief Returns the manhattan length of the vector. + */ + inline int manhattan () const + { + return ((xv > 0 ? xv : - xv) + (yv > 0 ? yv : - yv)); + } + + /** + * @fn Vr2i orthog () + * \brief Returns a CCW orthogonal vector. + */ + Vr2i orthog () const; + + /** + * @fn bool orientedAs (Vr2i v) + * \brief Returns true if v has the same orientation as the vector. + */ + inline bool orientedAs (Vr2i v) const + { + return (xv * v.xv + yv * v.yv >= 0); + } + + /** + * @fn void invert () + * \brief Inverts the vector. + */ + inline void invert () + { + xv = -xv; + yv = -yv; + } + + /** + * @fn bool *steps (int *n) + * \brief Returns the location of the steps between the vector ends. + * @param n size of the returned array. + */ + bool *steps (int *n) const; + + +private: + + /** Vector abscissae. */ + int xv; + /** Vector ordinate. */ + int yv; +}; + +#endif diff --git a/Code/Seg/Seg.pro b/Code/Seg/Seg.pro new file mode 100644 index 0000000000000000000000000000000000000000..c73224c9692062cac526aec58dd48e245e884b4b --- /dev/null +++ b/Code/Seg/Seg.pro @@ -0,0 +1,93 @@ +###################################################################### +# Automatically generated by qmake (3.0) mer. nov. 7 21:11:32 2018 +###################################################################### + +QT+=widgets +TEMPLATE = app +TARGET = Seg +INCLUDEPATH += . \ + BSTools \ + BlurredSegment \ + DirectionalScanner \ + ConvexHull \ + ImageTools +OBJECTS_DIR = obj + +# Input +HEADERS += BlurredSegment/biptlist.h \ + BlurredSegment/blurredsegment.h \ + BlurredSegment/blurredsegmentproto.h \ + BlurredSegment/bsdetector.h \ + BlurredSegment/bstracker.h \ + BlurredSegment/linespacefilter.h \ + BSTools/bsaccumulatoritem.h \ + BSTools/bsaccumulatorview.h \ + BSTools/bsdetectionwidget.h \ + BSTools/bsprofileitem.h \ + BSTools/bsprofileview.h \ + BSTools/bsstructureitem.h \ + BSTools/bsstructureview.h \ + BSTools/bswindow.h \ + ConvexHull/antipodal.h \ + ConvexHull/chvertex.h \ + ConvexHull/convexhull.h \ + DirectionalScanner/directionalscanner.h \ + DirectionalScanner/directionalscannero1.h \ + DirectionalScanner/directionalscannero2.h \ + DirectionalScanner/directionalscannero7.h \ + DirectionalScanner/directionalscannero8.h \ + DirectionalScanner/dynamicalscannero1.h \ + DirectionalScanner/dynamicalscannero2.h \ + DirectionalScanner/dynamicalscannero7.h \ + DirectionalScanner/dynamicalscannero8.h \ + DirectionalScanner/scannerprovider.h \ + DirectionalScanner/vhscannero1.h \ + DirectionalScanner/vhscannero2.h \ + DirectionalScanner/vhscannero7.h \ + DirectionalScanner/vhscannero8.h \ + ImageTools/absrat.h \ + ImageTools/digitalstraightline.h \ + ImageTools/digitalstraightsegment.h \ + ImageTools/pt2i.h \ + ImageTools/strucel.h \ + ImageTools/vmap.h \ + ImageTools/vr2i.h +SOURCES += main.cpp \ + BlurredSegment/biptlist.cpp \ + BlurredSegment/blurredsegment.cpp \ + BlurredSegment/blurredsegmentproto.cpp \ + BlurredSegment/bsdetector.cpp \ + BlurredSegment/bstracker.cpp \ + BlurredSegment/linespacefilter.cpp \ + BSTools/bsaccumulatoritem.cpp \ + BSTools/bsaccumulatorview.cpp \ + BSTools/bsdetectionwidget.cpp \ + BSTools/bsprofileitem.cpp \ + BSTools/bsprofileview.cpp \ + BSTools/bsstructureitem.cpp \ + BSTools/bsstructureview.cpp \ + BSTools/bswindow.cpp \ + ConvexHull/antipodal.cpp \ + ConvexHull/chvertex.cpp \ + ConvexHull/convexhull.cpp \ + DirectionalScanner/directionalscanner.cpp \ + DirectionalScanner/directionalscannero1.cpp \ + DirectionalScanner/directionalscannero2.cpp \ + DirectionalScanner/directionalscannero7.cpp \ + DirectionalScanner/directionalscannero8.cpp \ + DirectionalScanner/dynamicalscannero1.cpp \ + DirectionalScanner/dynamicalscannero2.cpp \ + DirectionalScanner/dynamicalscannero7.cpp \ + DirectionalScanner/dynamicalscannero8.cpp \ + DirectionalScanner/scannerprovider.cpp \ + DirectionalScanner/vhscannero1.cpp \ + DirectionalScanner/vhscannero2.cpp \ + DirectionalScanner/vhscannero7.cpp \ + DirectionalScanner/vhscannero8.cpp \ + ImageTools/absrat.cpp \ + ImageTools/digitalstraightline.cpp \ + ImageTools/digitalstraightsegment.cpp \ + ImageTools/pt2i.cpp \ + ImageTools/strucel.cpp \ + ImageTools/vmap.cpp \ + ImageTools/vr2i.cpp diff --git a/Code/Seg/main.cpp b/Code/Seg/main.cpp new file mode 100755 index 0000000000000000000000000000000000000000..ada2ffd8113b16bf7c14cf79b22587f5efcd1f1d --- /dev/null +++ b/Code/Seg/main.cpp @@ -0,0 +1,68 @@ +#include <QApplication> +#include <string> +#include "bswindow.h" +// #include "scanwindow.h" + + +int main (int argc, char *argv[]) +{ + int val = 0; + int imageName = 0; + bool testing = false; + QApplication app (argc, argv); + +/* + if (argc == 2 && string (argv[1]) == string ("scans")) + { + ScanWindow sw (&val); + sw.setWindowTitle ("Scans"); + sw.show (); + return app.exec (); + } +*/ + + BSWindow window (&val); // val : necessary argument ! + for (int i = 1; i < argc; i++) + { + if (string(argv[i]).at(0) == '-') + { + if (string(argv[i]) == string ("-profile")) window.toggleProfWindow (); + else if (string(argv[i]) == string ("-accu")) window.toggleAccuWindow (); + else if (string(argv[i]) == string ("-seg")) window.toggleSegWindow (); + // Test command : time ./Seg -test ../Images/couloir.jpg + else if (string(argv[i]) == string ("-test")) testing = true; + else if (string(argv[i]) == string ("-sobel3x3")) + window.useGradient (VMap::TYPE_SOBEL_3X3); + else if (string(argv[i]) == string ("-sobel5x5")) + window.useGradient (VMap::TYPE_SOBEL_5X5); + else if (string(argv[i]) == string ("-tophat")) + window.useGradient (VMap::TYPE_TOP_HAT); + else if (string(argv[i]) == string ("-blackhat")) + window.useGradient (VMap::TYPE_BLACK_HAT); + else if (string(argv[i]) == string ("-morpho")) + window.useGradient (VMap::TYPE_MORPHO); + else + { + int l = string (argv[i]).length (); + for (int j = 1; j < l; j++) + { + char carac = string(argv[i]).at(j); + if (carac == 'p') window.toggleProfWindow (); + else if (carac == 'a') window.toggleAccuWindow (); + else if (carac == 's') window.toggleSegWindow (); + } + } + } + else imageName = i; + } + if (imageName != 0) window.setFile (QString (argv[imageName])); + else window.setFile (QString ("../couloir.gif")); + if (testing) + { + window.runTest (); + return (EXIT_SUCCESS); + } + window.runOptions (); + window.show (); + return app.exec (); +}