1 /*
2 SPDX-FileCopyrightText: 2019 Patrick Molenaar <pr_molenaar@hotmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "houghline.h"
8
9 #include <qmath.h>
10 #include <QVector>
11 #include <fits_debug.h>
12
13 /**
14 * Initialises the hough line
15 */
HoughLine(double theta,double r,int width,int height,int score)16 HoughLine::HoughLine(double theta, double r, int width, int height, int score)
17 : QLineF()
18 {
19 this->theta = theta;
20 this->r = r;
21 this->score = score;
22
23 setP1(RotatePoint(0, r, theta, width, height));
24 setP2(RotatePoint(width - 1, r, theta, width, height));
25 }
26
RotatePoint(int x1,double r,double theta,int width,int height)27 QPointF HoughLine::RotatePoint(int x1, double r, double theta, int width, int height)
28 {
29 int hx, hy;
30
31 hx = qFloor((width + 1) / 2.0);
32 hy = qFloor((height + 1) / 2.0);
33
34 double sinAngle = qSin(-theta);
35 double cosAngle = qCos(-theta);
36
37 // translate point back to origin:
38 double x2 = x1 - hx;
39 double y2 = r - hy;
40
41 // rotate point
42 double xnew = x2 * cosAngle - y2 * sinAngle;
43 double ynew = x2 * sinAngle + y2 * cosAngle;
44
45 // translate point back:
46 x2 = xnew + hx;
47 y2 = ynew + hy;
48
49 return QPointF(x2, y2);
50 }
51
getScore() const52 int HoughLine::getScore() const
53 {
54 return score;
55 }
56
getR() const57 double HoughLine::getR() const
58 {
59 return r;
60 }
61
getTheta() const62 double HoughLine::getTheta() const
63 {
64 return theta;
65 }
66
setTheta(const double theta)67 void HoughLine::setTheta(const double theta)
68 {
69 this->theta = theta;
70 }
71
compareByScore(const HoughLine * line1,const HoughLine * line2)72 bool HoughLine::compareByScore(const HoughLine *line1,const HoughLine *line2)
73 {
74 return (line1->getScore() < line2->getScore());
75 }
76
compareByTheta(const HoughLine * line1,const HoughLine * line2)77 bool HoughLine::compareByTheta(const HoughLine *line1,const HoughLine *line2)
78 {
79 return (line1->getTheta() < line2->getTheta());
80 }
81
printHoughLine()82 void HoughLine::printHoughLine()
83 {
84 qCDebug(KSTARS_FITS) << "Houghline: [score: " << score << ", r: " << r << ", theta: " << theta << " [rad]="
85 << (theta * 180.0 / M_PI) << " [deg], p1: " << p1().x() << ", " << p1().y() << ", p2: "
86 << p2().x() << ", " << p2().y() << "]";
87 }
88
89 /**
90 * Sources for intersection and distance calculations came from
91 * http://paulbourke.net/geometry/pointlineplane/
92 * Also check https://doc.qt.io/archives/qt-4.8/qlinef.html for more line methods
93 */
Intersect(const HoughLine & other_line,QPointF & intersection)94 HoughLine::IntersectResult HoughLine::Intersect(const HoughLine& other_line, QPointF& intersection)
95 {
96 double denom = ((other_line.p2().y() - other_line.p1().y()) * (p2().x() - p1().x())) -
97 ((other_line.p2().x() - other_line.p1().x()) * (p2().y() - p1().y()));
98
99 double nume_a = ((other_line.p2().x() - other_line.p1().x()) * (p1().y() - other_line.p1().y())) -
100 ((other_line.p2().y() - other_line.p1().y()) * (p1().x() - other_line.p1().x()));
101
102 double nume_b = ((p2().x() - p1().x()) * (p1().y() - other_line.p1().y())) -
103 ((p2().y() - p1().y()) * (p1().x() - other_line.p1().x()));
104
105 if (denom == 0.0f)
106 {
107 if(nume_a == 0.0f && nume_b == 0.0f)
108 {
109 return COINCIDENT;
110 }
111 return PARALLEL;
112 }
113
114 double ua = nume_a / denom;
115 double ub = nume_b / denom;
116
117 if(ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f)
118 {
119 // Get the intersection point.
120 intersection.setX(qreal(p1().x() + ua * (p2().x() - p1().x())));
121 intersection.setY(qreal(p1().y() + ua * (p2().y() - p1().y())));
122 return INTERESECTING;
123 }
124
125 return NOT_INTERESECTING;
126 }
127
Magnitude(const QPointF & point1,const QPointF & point2)128 double HoughLine::Magnitude(const QPointF& point1, const QPointF& point2)
129 {
130 QPointF vector = point2 - point1;
131 return qSqrt(vector.x() * vector.x() + vector.y() * vector.y());
132 }
133
DistancePointLine(const QPointF & point,QPointF & intersection,double & distance)134 bool HoughLine::DistancePointLine(const QPointF& point, QPointF& intersection, double& distance)
135 {
136 double lineMag = length();
137
138 double U = qreal((((point.x() - p1().x()) * (p2().x() - p1().x())) +
139 ((point.y() - p1().y()) * (p2().y() - p1().y()))) /
140 (lineMag * lineMag));
141
142 if (U < 0.0 || U > 1.0) {
143 return false; // closest point does not fall within the line segment
144 }
145
146 intersection.setX(p1().x() + U * (p2().x() - p1().x()));
147 intersection.setY(p1().y() + U * (p2().y() - p1().y()));
148
149 distance = Magnitude(point, intersection);
150
151 return true;
152 }
153
Offset(const int offsetX,const int offsetY)154 void HoughLine::Offset(const int offsetX, const int offsetY)
155 {
156 setP1(QPointF(p1().x() + offsetX, p1().y() + offsetY));
157 setP2(QPointF(p2().x() + offsetX, p2().y() + offsetY));
158 }
159
getSortedTopThreeLines(QVector<HoughLine * > & houghLines,QVector<HoughLine * > & top3Lines)160 void HoughLine::getSortedTopThreeLines(QVector<HoughLine*> &houghLines, QVector<HoughLine*> &top3Lines)
161 {
162 // Sort houghLines by score (highest scores are clearest lines)
163 // For use of sort compare methods see: https://www.off-soft.net/en/develop/qt/qtb1.html
164 qSort(houghLines.begin(), houghLines.end(), HoughLine::compareByScore);
165
166 // Get top three lines (these should represent the three lines matching the bahtinov mask lines
167 top3Lines = houghLines.mid(0, 3);
168 // Verify the angle of these lines with regard to the bahtinov mask angle, correct the angle if necessary
169 HoughLine* lineR = top3Lines[0];
170 HoughLine* lineG = top3Lines[1];
171 HoughLine* lineB = top3Lines[2];
172 double thetaR = lineR->getTheta();
173 double thetaG = lineG->getTheta();
174 double thetaB = lineB->getTheta();
175 // Calculate angle between each line
176 double bahtinovMaskAngle = qDegreesToRadians(20.0 + 5.0); // bahtinov mask angle plus 5 degree margin
177 double dGR = thetaR - thetaG;
178 double dBG = thetaB - thetaG;
179 double dBR = thetaB - thetaR;
180 if (dGR > bahtinovMaskAngle && dBR > bahtinovMaskAngle) {
181 // lineR has theta that is 180 degrees rotated
182 thetaR -= M_PI;
183 // update theta
184 lineR->setTheta(thetaR);
185 }
186 if (dBR > bahtinovMaskAngle && dBG > bahtinovMaskAngle) {
187 // lineB has theta that is 180 degrees rotated
188 thetaB -= M_PI;
189 // update theta
190 lineB->setTheta(thetaB);
191 }
192 if (dGR > bahtinovMaskAngle && dBG > bahtinovMaskAngle) {
193 // lineG has theta that is 180 degrees rotated
194 thetaG -= M_PI;
195 // update theta
196 lineG->setTheta(thetaG);
197 }
198 // Now sort top3lines array according to calculated new angles
199 qSort(top3Lines.begin(),top3Lines.end(), HoughLine::compareByTheta);
200 }
201