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 }