Skip to content

Commit b2df3a7

Browse files
committed
refactor center line calculation
this is now stored in the result for easier access (will also be re-used in subsequent step, this way we don't need to calculate it several times).
1 parent adb210f commit b2df3a7

File tree

4 files changed

+77
-44
lines changed

4 files changed

+77
-44
lines changed

include/banana-lib/lib.hpp

+35-10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ namespace banana {
1414
typedef std::vector<cv::Point> Contour;
1515
/// List of multiple contours.
1616
typedef std::vector<Contour> Contours;
17+
/// The coefficients a0, a1 and a2 of the two-dimensional polynomial $y = a0 + a1 * x + a2 * x^2$.
18+
typedef std::tuple<double, double, double> Polynomial2DCoefficients;
1719

1820
/**
1921
* All errors which may occur during analysis of the image.
@@ -51,17 +53,29 @@ namespace banana {
5153
* The analysis results for a banana which has been found in the image.
5254
*/
5355
struct AnalysisResult {
56+
struct CenterLine {
57+
/**
58+
* The coefficients a0, a1 and a2 of the two-dimensional polynomial describing the center line of the banana.
59+
* Important: note that this is given along the primary axis of the banana and not in relation to the x-axis of the image.
60+
*
61+
* The center line is defined approximately by the method $y = a0 + a1 * x + a2 * x^2$.
62+
*/
63+
Polynomial2DCoefficients coefficients;
64+
65+
/**
66+
* The points along the center line inside of the banana contour (rotated, banana coordinate system).
67+
*/
68+
std::vector<cv::Point2d> points_in_banana_coordsys;
69+
};
70+
5471
/// Contour of the banana in the image.
5572
Contour contour;
56-
/**
57-
* The coefficients a0, a1 and a2 of the two-dimensional polynomial describing the center line of the banana.
58-
* Important: note that this is given along the primary axis of the banana and not in relation to the x-axis of the image.
59-
*
60-
* The center line is defined approximately by the method $y = a0 + a1 * x + a2 * x^2$.
61-
*/
62-
std::tuple<double, double, double> center_line_coefficients;
73+
74+
CenterLine center_line;
75+
6376
/// The rotation angle of the banana, as seen from the x-axis. Given in radians.
6477
double rotation_angle;
78+
6579
/// The estimated center of the banana shape. Note that this might actually lie outside of the banana itself due to the curvature!
6680
cv::Point estimated_center;
6781
};
@@ -154,12 +168,22 @@ namespace banana {
154168
* Calculate the coefficients of the two-dimensional polynomial describing the center line.
155169
* Important: note that this is given along the primary axis of the banana and not in relation to the x-axis of the image.
156170
*
157-
* @param banana_contour the contour of the banana to be analysed
171+
* @param rotated_banana_contour the contour of the banana to be analysed - already rotated so that the x-axis is along the primary axis of the banana.
158172
* @param pca_result the result from the PCA analysis
159173
* @return the coefficients of the two-dimensional polynomial describing the center line of the banana.
160174
*/
161175
[[nodiscard]]
162-
auto GetBananaCenterLineCoefficients(Contour const& banana_contour, PCAResult const& pca_result) const -> std::expected<std::tuple<double, double, double>, AnalysisError>;
176+
auto GetBananaCenterLineCoefficients(Contour const& rotated_banana_contour) const -> std::expected<Polynomial2DCoefficients, AnalysisError>;
177+
178+
/**
179+
* Calculate the center line of the banana in the coordinate system of the banana (x-axis along the primary axis of the banana).
180+
*
181+
* @param rotated_banana_contour the contour of the banana to be analysed - already rotated so that the x-axis is along the primary axis of the banana.
182+
* @param coefficients the coefficients of the two-dimensional polynomial describing the center line of the banana
183+
* @return a consecutive list of points with 1px spacing on the x-axis along the whole x-axis of the banana, describing the center line.
184+
*/
185+
[[nodiscard]]
186+
auto GetBananaCenterLine(Contour const& rotated_banana_contour, Polynomial2DCoefficients const& coefficients) const -> std::vector<cv::Point2d>;
163187

164188
/**
165189
* Rotate a contour by the defined angle around the specified point.
@@ -194,9 +218,10 @@ namespace banana {
194218
auto AnalyzeBanana(cv::Mat const& image, Contour const& banana_contour) const -> std::expected<AnalysisResult, AnalysisError>;
195219

196220
/**
197-
* Plot the center line of the banana - as defined by the coefficients & contour - onto the provided draw target.
221+
* Plot the center line of the banana onto the provided draw target.
198222
*
199223
* @param draw_target the image being annotated.
224+
* @param center_line the center line along the whole x-axis of the banana in the coordinate system of the x-axis
200225
* @param result the analysis result for the banana to be drawn.
201226
*/
202227
void PlotCenterLine(cv::Mat& draw_target, AnalysisResult const& result) const;

src/lib.cpp

+37-30
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ namespace banana {
4040
std::ostream& operator << (std::ostream& o, AnnotatedAnalysisResult const& analysis_result) {
4141
o << "found " << analysis_result.banana.size() << " banana(s) in the picture" << std::endl;
4242
for (auto const& [n, banana] : std::ranges::enumerate_view(analysis_result.banana)) {
43-
auto const& [coeff_0, coeff_1, coeff_2] = banana.center_line_coefficients;
43+
auto const& [coeff_0, coeff_1, coeff_2] = banana.center_line.coefficients;
4444
o << " Banana #" << n << ":" << std::endl;
4545
o << " " << std::format("y = {} + {} * x + {} * x^2", coeff_0, coeff_1, coeff_2) << std::endl;
4646
o << " Rotation = " << (banana.rotation_angle * 180 / std::numbers::pi) << " degrees" << std::endl;
@@ -129,15 +129,31 @@ namespace banana {
129129
return contours;
130130
}
131131

132-
auto Analyzer::GetBananaCenterLineCoefficients(Contour const& banana_contour, PCAResult const& pca_result) const -> std::expected<std::tuple<double, double, double>, AnalysisError> {
133-
// rotate the contour so that it's horizontal
134-
auto const rotated_contour = this->RotateContour(banana_contour, pca_result.center, pca_result.angle);
135-
132+
auto Analyzer::GetBananaCenterLineCoefficients(Contour const& rotated_banana_contour) const -> std::expected<Polynomial2DCoefficients, AnalysisError> {
136133
auto const to_std_pair_fn = [](auto const& p) -> std::pair<double, double> { return {p.x, p.y}; };
137-
auto const coeffs = polyfit::Fit2DPolynomial(rotated_contour | std::views::transform(to_std_pair_fn));
134+
auto const coeffs = polyfit::Fit2DPolynomial(rotated_banana_contour | std::views::transform(to_std_pair_fn));
138135
return coeffs.transform_error([](auto const& _) -> auto {return AnalysisError::kPolynomialCalcFailure;});
139136
}
140137

138+
auto Analyzer::GetBananaCenterLine(Contour const& rotated_banana_contour, Polynomial2DCoefficients const& coefficients) const -> std::vector<cv::Point2d> {
139+
// note that the coefficients for the center line are given in relation to the bananas main axis.
140+
// accordingly we have to rotate the resulting line to plot it over the banana in the image.
141+
142+
auto const& [coeff_0, coeff_1, coeff_2] = coefficients;
143+
144+
auto const minmax_x = std::ranges::minmax(rotated_banana_contour | std::views::transform(&cv::Point::x));
145+
146+
/// Calculate a Point2d for the [x,y] coords based on the provided polynomial and x-values.
147+
auto const calc_xy = [&coeff_0, &coeff_1, &coeff_2](auto const&& x) -> cv::Point2d {
148+
auto const y = coeff_0 + coeff_1 * x + coeff_2 * x * x;
149+
return {static_cast<double>(x), y};
150+
};
151+
152+
return std::views::iota(minmax_x.min, minmax_x.max)
153+
| std::views::transform(calc_xy)
154+
| std::ranges::to<std::vector>();
155+
}
156+
141157
auto Analyzer::RotateContour(Contour const& contour, cv::Point const& center, double const angle) const -> Contour {
142158
auto const rotation_matrix = cv::getRotationMatrix2D(center, angle * 180 / std::numbers::pi, 1);
143159
Contour rotated_contour{contour.size()};
@@ -181,42 +197,33 @@ namespace banana {
181197

182198
auto Analyzer::AnalyzeBanana(cv::Mat const& image, Contour const& banana_contour) const -> std::expected<AnalysisResult, AnalysisError> {
183199
auto const pca = this->GetPCA(banana_contour);
184-
auto const coeffs = this->GetBananaCenterLineCoefficients(banana_contour, pca);
200+
201+
// rotate the contour so that it's horizontal
202+
auto const rotated_contour = this->RotateContour(banana_contour, pca.center, pca.angle);
203+
204+
auto const coeffs = this->GetBananaCenterLineCoefficients(rotated_contour);
185205
if (!coeffs) {
186206
return std::unexpected{coeffs.error()};
187207
}
188208

209+
auto const center_line_points_in_banana_coordsys = this->GetBananaCenterLine(rotated_contour, *coeffs);
210+
189211
return AnalysisResult{
190212
.contour = banana_contour,
191-
.center_line_coefficients = *coeffs,
213+
.center_line{
214+
.coefficients = *coeffs,
215+
.points_in_banana_coordsys = center_line_points_in_banana_coordsys,
216+
},
192217
.rotation_angle = pca.angle,
193218
.estimated_center = pca.center,
194219
};
195220
}
196221

197222
void Analyzer::PlotCenterLine(cv::Mat& draw_target, AnalysisResult const& result) const {
198-
// note that the coefficients for the center line are given in relation to the bananas main axis.
199-
// accordingly we have to rotate the resulting line to plot it over the banana in the image.
200-
201-
// rotate the contour so that it's horizontal (needed to calculate the x-axis points for plotting in the coordinate system of the banana).
202-
auto const rotated_contour = this->RotateContour(result.contour, result.estimated_center, result.rotation_angle);
203-
204-
auto const& [coeff_0, coeff_1, coeff_2] = result.center_line_coefficients;
205-
206-
auto const minmax_x = std::ranges::minmax(rotated_contour | std::views::transform(&cv::Point::x));
207-
208-
/// Calculate a Point2d for the [x,y] coords based on the provided polynomial and x-values.
209-
auto const calc_xy = [&coeff_0, &coeff_1, &coeff_2](auto const&& x) -> cv::Point2d {
210-
auto const y = coeff_0 + coeff_1 * x + coeff_2 * x * x;
211-
return {static_cast<double>(x), y};
212-
};
213-
auto const to_point2i = [](auto const&& p) -> cv::Point {return {static_cast<int>(p.x), static_cast<int>(p.y)};};
214-
215-
auto const line_extension_length = 50; ///< amount of pixels by which the line should be extended on either side
216-
auto const start = std::max(minmax_x.min - line_extension_length, 0);
217-
auto const end = std::min(minmax_x.max + line_extension_length, draw_target.cols);
218-
auto const center_line_points = std::views::iota(start, end) | std::views::transform(calc_xy);
219-
auto const center_line_points2i = center_line_points | std::views::transform(to_point2i) | std::ranges::to<std::vector>();
223+
auto const to_point2i = [](auto const& p) -> cv::Point {return {static_cast<int>(p.x), static_cast<int>(p.y)};};
224+
auto const center_line_points2i = result.center_line.points_in_banana_coordsys
225+
| std::views::transform(to_point2i)
226+
| std::ranges::to<std::vector>();
220227

221228
// rotate the center line back so that it fits on the image
222229
auto const rotated_center_line = this->RotateContour(center_line_points2i, result.estimated_center, -result.rotation_angle);

test/banana-lib-test.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ TEST(BananaContourFinderTestSuite, FindTwoBananas) {
5151

5252
TEST(CenterLineCoefficientsTestSuite, SingleBanana00) {
5353
GET_RESULT("resources/test-images/banana-00.jpg", 1);
54-
ASSERT_COEFFS_NEAR(2536.0389294, -1.8237497, 0.0005237, result.banana.front().center_line_coefficients);
54+
ASSERT_COEFFS_NEAR(2482.2342194, -1.8133667, 0.0005347, result.banana.front().center_line.coefficients);
5555
}
5656

5757
TEST(PCATestSuite, SingleBanana00) {

test/polyfit-test-util.hpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33

44
#define ASSERT_COEFFS_NEAR(coeff_expected_0, coeff_expected_1, coeff_expected_2, coeffs) \
55
do { \
6-
ASSERT_NEAR(coeff_expected_0, std::get<0>(coeffs), 1e-6); \
7-
ASSERT_NEAR(coeff_expected_1, std::get<1>(coeffs), 1e-6); \
8-
ASSERT_NEAR(coeff_expected_2, std::get<2>(coeffs), 1e-6); \
6+
auto const& [coeff_0, coeff_1, coeff_2] = coeffs; \
7+
ASSERT_NEAR(coeff_expected_0, coeff_0, 1e-6); \
8+
ASSERT_NEAR(coeff_expected_1, coeff_1, 1e-6); \
9+
ASSERT_NEAR(coeff_expected_2, coeff_2, 1e-6); \
910
} while (false)
1011

1112
#endif //BANANA_PROJECT_POLYFIT_TEST_UTIL_HPP

0 commit comments

Comments
 (0)