File indexing completed on 2024-05-05 15:55:46

0001 /*
0002     SPDX-FileCopyrightText: 2019 Patrick Molenaar <pr_molenaar@hotmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "houghline.h"
0008 
0009 #include <qmath.h>
0010 #include <QVector>
0011 #include <fits_debug.h>
0012 
0013 /**
0014  * Initialises the hough line
0015  */
0016 HoughLine::HoughLine(double theta, double r, int width, int height, int score)
0017     : QLineF()
0018 {
0019     this->theta = theta;
0020     this->r = r;
0021     this->score = score;
0022 
0023     setP1(RotatePoint(0, r, theta, width, height));
0024     setP2(RotatePoint(width - 1, r, theta, width, height));
0025 }
0026 
0027 QPointF HoughLine::RotatePoint(int x1, double r, double theta, int width, int height)
0028 {
0029     int hx, hy;
0030 
0031     hx = qFloor((width + 1) / 2.0);
0032     hy = qFloor((height + 1) / 2.0);
0033 
0034     double sinAngle = qSin(-theta);
0035     double cosAngle = qCos(-theta);
0036 
0037     // translate point back to origin:
0038     double x2 = x1 - hx;
0039     double y2 = r - hy;
0040 
0041     // rotate point
0042     double xnew = x2 * cosAngle - y2 * sinAngle;
0043     double ynew = x2 * sinAngle + y2 * cosAngle;
0044 
0045     // translate point back:
0046     x2 = xnew + hx;
0047     y2 = ynew + hy;
0048 
0049     return QPointF(x2, y2);
0050 }
0051 
0052 int HoughLine::getScore() const
0053 {
0054     return score;
0055 }
0056 
0057 double HoughLine::getR() const
0058 {
0059     return r;
0060 }
0061 
0062 double HoughLine::getTheta() const
0063 {
0064     return theta;
0065 }
0066 
0067 void HoughLine::setTheta(const double theta)
0068 {
0069     this->theta = theta;
0070 }
0071 
0072 bool HoughLine::compareByScore(const HoughLine *line1,const HoughLine *line2)
0073 {
0074     return (line1->getScore() < line2->getScore());
0075 }
0076 
0077 bool HoughLine::compareByTheta(const HoughLine *line1,const HoughLine *line2)
0078 {
0079     return (line1->getTheta() < line2->getTheta());
0080 }
0081 
0082 void HoughLine::printHoughLine()
0083 {
0084     qCDebug(KSTARS_FITS) << "Houghline: [score: " << score << ", r: " << r << ", theta: " << theta << " [rad]="
0085                          << (theta * 180.0 / M_PI) << " [deg], p1: " << p1().x() << ", " << p1().y() << ", p2: "
0086                          << p2().x() << ", " << p2().y() << "]";
0087 }
0088 
0089 /**
0090  * Sources for intersection and distance calculations came from
0091  *     http://paulbourke.net/geometry/pointlineplane/
0092  * Also check https://doc.qt.io/archives/qt-4.8/qlinef.html for more line methods
0093  */
0094 HoughLine::IntersectResult HoughLine::Intersect(const HoughLine& other_line, QPointF& intersection)
0095 {
0096     double denom = ((other_line.p2().y() - other_line.p1().y()) * (p2().x() - p1().x())) -
0097             ((other_line.p2().x() - other_line.p1().x()) * (p2().y() - p1().y()));
0098 
0099     double nume_a = ((other_line.p2().x() - other_line.p1().x()) * (p1().y() - other_line.p1().y())) -
0100             ((other_line.p2().y() - other_line.p1().y()) * (p1().x() - other_line.p1().x()));
0101 
0102     double nume_b = ((p2().x() - p1().x()) * (p1().y() - other_line.p1().y())) -
0103             ((p2().y() - p1().y()) * (p1().x() - other_line.p1().x()));
0104 
0105     if (denom == 0.0f)
0106     {
0107         if(nume_a == 0.0f && nume_b == 0.0f)
0108         {
0109             return COINCIDENT;
0110         }
0111         return PARALLEL;
0112     }
0113 
0114     double ua = nume_a / denom;
0115     double ub = nume_b / denom;
0116 
0117     if(ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f)
0118     {
0119         // Get the intersection point.
0120         intersection.setX(qreal(p1().x() + ua * (p2().x() - p1().x())));
0121         intersection.setY(qreal(p1().y() + ua * (p2().y() - p1().y())));
0122         return INTERESECTING;
0123     }
0124 
0125     return NOT_INTERESECTING;
0126 }
0127 
0128 double HoughLine::Magnitude(const QPointF& point1, const QPointF& point2)
0129 {
0130     QPointF vector = point2 - point1;
0131     return qSqrt(vector.x() * vector.x() + vector.y() * vector.y());
0132 }
0133 
0134 bool HoughLine::DistancePointLine(const QPointF& point, QPointF& intersection, double& distance)
0135 {
0136     double lineMag = length();
0137 
0138     double U = qreal((((point.x() - p1().x()) * (p2().x() - p1().x())) +
0139             ((point.y() - p1().y()) * (p2().y() - p1().y()))) /
0140             (lineMag * lineMag));
0141 
0142     if (U < 0.0 || U > 1.0) {
0143         return false; // closest point does not fall within the line segment
0144     }
0145 
0146     intersection.setX(p1().x() + U * (p2().x() - p1().x()));
0147     intersection.setY(p1().y() + U * (p2().y() - p1().y()));
0148 
0149     distance = Magnitude(point, intersection);
0150 
0151     return true;
0152 }
0153 
0154 void HoughLine::Offset(const int offsetX, const int offsetY)
0155 {
0156     setP1(QPointF(p1().x() + offsetX, p1().y() + offsetY));
0157     setP2(QPointF(p2().x() + offsetX, p2().y() + offsetY));
0158 }
0159 
0160 void HoughLine::getSortedTopThreeLines(QVector<HoughLine*> &houghLines, QVector<HoughLine*> &top3Lines)
0161 {
0162     // Sort houghLines by score (highest scores are clearest lines)
0163     // For use of sort compare methods see: https://www.off-soft.net/en/develop/qt/qtb1.html
0164     std::sort(houghLines.begin(), houghLines.end(), HoughLine::compareByScore);
0165 
0166     // Get top three lines (these should represent the three lines matching the bahtinov mask lines
0167     top3Lines = houghLines.mid(0, 3);
0168     // Verify the angle of these lines with regard to the bahtinov mask angle, correct the angle if necessary
0169     HoughLine* lineR = top3Lines[0];
0170     HoughLine* lineG = top3Lines[1];
0171     HoughLine* lineB = top3Lines[2];
0172     double thetaR = lineR->getTheta();
0173     double thetaG = lineG->getTheta();
0174     double thetaB = lineB->getTheta();
0175     // Calculate angle between each line
0176     double bahtinovMaskAngle = qDegreesToRadians(20.0 + 5.0); // bahtinov mask angle plus 5 degree margin
0177     double dGR = thetaR - thetaG;
0178     double dBG = thetaB - thetaG;
0179     double dBR = thetaB - thetaR;
0180     if (dGR > bahtinovMaskAngle && dBR > bahtinovMaskAngle) {
0181         // lineR has theta that is 180 degrees rotated
0182         thetaR -= M_PI;
0183         // update theta
0184         lineR->setTheta(thetaR);
0185     }
0186     if (dBR > bahtinovMaskAngle && dBG > bahtinovMaskAngle) {
0187         // lineB has theta that is 180 degrees rotated
0188         thetaB -= M_PI;
0189         // update theta
0190         lineB->setTheta(thetaB);
0191     }
0192     if (dGR > bahtinovMaskAngle && dBG > bahtinovMaskAngle) {
0193         // lineG has theta that is 180 degrees rotated
0194         thetaG -= M_PI;
0195         // update theta
0196         lineG->setTheta(thetaG);
0197     }
0198     // Now sort top3lines array according to calculated new angles
0199     std::sort(top3Lines.begin(),top3Lines.end(), HoughLine::compareByTheta);
0200 }