LCOV - code coverage report
Current view: top level - sources/inference - LanePostProcessor.cpp (source / functions) Hit Total Coverage
Test: filtered.info Lines: 2 92 2.2 %
Date: 2025-08-07 15:31:19 Functions: 1 8 12.5 %

          Line data    Source code
       1             : #include "../../includes/inference/LanePostProcessor.hpp"
       2             : #include <opencv2/cudaarithm.hpp>
       3             : #include <cmath>
       4             : #include <numeric>
       5             : 
       6          23 : LanePostProcessor::LanePostProcessor(int minArea, int minLength, float angleThresh, float mergeDist)
       7             :         : minComponentSize(minArea),
       8             :           minComponentLength(minLength),
       9             :           angleThreshold(angleThresh),
      10          23 :           mergeDistance(mergeDist) {}
      11             : 
      12           0 : cv::cuda::GpuMat LanePostProcessor::process(const cv::cuda::GpuMat& rawMaskGpu) {
      13           0 :         cv::cuda::GpuMat binaryMaskGpu;
      14           0 :         cv::cuda::threshold(rawMaskGpu, binaryMaskGpu, 0.5, 255.0, cv::THRESH_BINARY);
      15           0 :         binaryMaskGpu.convertTo(binaryMaskGpu, CV_8U);
      16             : 
      17           0 :         cv::Mat binaryMask;
      18           0 :         binaryMaskGpu.download(binaryMask); // CPU copy for contour analysis
      19             : 
      20           0 :         std::vector<LaneInfo> initialLanes = extractLaneInfo(binaryMask, false, true);
      21             : 
      22           0 :         cv::Mat mergedMask;
      23           0 :         cv::cvtColor(binaryMask, mergedMask, cv::COLOR_GRAY2BGR);
      24           0 :         drawLaneConnections(initialLanes, mergedMask);
      25             : 
      26           0 :         cv::Mat grayMerged;
      27           0 :         cv::cvtColor(mergedMask, grayMerged, cv::COLOR_BGR2GRAY);
      28             : 
      29           0 :         std::vector<LaneInfo> finalLanes = extractLaneInfo(grayMerged, true, false);
      30           0 :         cv::Mat filtered = renderFilteredMask(finalLanes, binaryMask.size());
      31             : 
      32           0 :         cv::cuda::GpuMat resultGpu;
      33           0 :         resultGpu.upload(filtered);
      34           0 :         return resultGpu;
      35             : }
      36             : 
      37           0 : std::vector<LanePostProcessor::LaneInfo> LanePostProcessor::extractLaneInfo(const cv::Mat& mask, bool filterSize, bool filterLength) {
      38           0 :         std::vector<std::vector<cv::Point>> contours;
      39           0 :         cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
      40             : 
      41           0 :         std::vector<LaneInfo> lanes;
      42           0 :         int height = mask.rows, width = mask.cols;
      43             : 
      44           0 :         for (const auto& cnt : contours) {
      45           0 :                 double area = cv::contourArea(cnt);
      46           0 :                 double length = cv::arcLength(cnt, false);
      47             : 
      48           0 :                 if ((!filterSize || area > minComponentSize) &&
      49           0 :                         (!filterLength || length > minComponentLength) &&
      50           0 :                         cnt.size() >= 2) {
      51             : 
      52           0 :                         cv::Vec4f line;
      53           0 :                         cv::fitLine(cnt, line, cv::DIST_L2, 0, 0.01, 0.01);
      54           0 :                         float vx = line[0], vy = line[1], x0 = line[2], y0 = line[3];
      55           0 :                         float angle = std::fmod(std::atan2(vy, vx) * 180.0 / CV_PI, 180.0f);
      56             : 
      57           0 :                         cv::Moments M = cv::moments(cnt);
      58           0 :                         if (M.m00 == 0) continue;
      59           0 :                         cv::Point centroid(M.m10 / M.m00, M.m01 / M.m00);
      60             : 
      61           0 :                         std::vector<cv::Point> intersections;
      62           0 :                         if (vy != 0) {
      63           0 :                                 float t = -y0 / vy;
      64           0 :                                 float x = x0 + vx * t;
      65           0 :                                 if (x >= 0 && x < width) intersections.emplace_back(x, 0);
      66           0 :                                 t = (height - 1 - y0) / vy;
      67           0 :                                 x = x0 + vx * t;
      68           0 :                                 if (x >= 0 && x < width) intersections.emplace_back(x, height - 1);
      69             :                         }
      70           0 :                         if (vx != 0) {
      71           0 :                                 float t = -x0 / vx;
      72           0 :                                 float y = y0 + vy * t;
      73           0 :                                 if (y >= 0 && y < height) intersections.emplace_back(0, y);
      74           0 :                                 t = (width - 1 - x0) / vx;
      75           0 :                                 y = y0 + vy * t;
      76           0 :                                 if (y >= 0 && y < height) intersections.emplace_back(width - 1, y);
      77             :                         }
      78             : 
      79           0 :                         if (intersections.size() >= 2) {
      80           0 :                                 lanes.push_back({ static_cast<int>(lanes.size() + 1), angle, centroid, {intersections[0], intersections[1]}, cnt });
      81             :                         }
      82             :                 }
      83             :         }
      84             : 
      85           0 :         return lanes;
      86             : }
      87             : 
      88           0 : bool LanePostProcessor::shouldMerge(const LaneInfo& a, const LaneInfo& b) const {
      89           0 :         float angleDiff = std::fabs(a.angle - b.angle);
      90           0 :         angleDiff = std::min(angleDiff, 180.0f - angleDiff);
      91             : 
      92           0 :         if (angleDiff < angleThreshold) {
      93           0 :                 for (const auto& pt1 : a.targets) {
      94           0 :                         for (const auto& pt2 : b.targets) {
      95           0 :                                 if (cv::norm(pt1 - pt2) < mergeDistance)
      96           0 :                                         return true;
      97             :                         }
      98             :                 }
      99             :         }
     100           0 :         return false;
     101             : }
     102             : 
     103           0 : std::pair<cv::Point, cv::Point> LanePostProcessor::getExtremities(const std::vector<cv::Point>& contour) const {
     104           0 :         double maxDist = 0;
     105           0 :         cv::Point ext1 = contour[0], ext2 = contour[0];
     106             : 
     107           0 :         for (size_t i = 0; i < contour.size(); ++i) {
     108           0 :                 for (size_t j = i + 1; j < contour.size(); ++j) {
     109           0 :                         double dist = cv::norm(contour[i] - contour[j]);
     110           0 :                         if (dist > maxDist) {
     111           0 :                                 maxDist = dist;
     112           0 :                                 ext1 = contour[i];
     113           0 :                                 ext2 = contour[j];
     114             :                         }
     115             :                 }
     116             :         }
     117             : 
     118           0 :         return {ext1, ext2};
     119             : }
     120             : 
     121           0 : void LanePostProcessor::drawLaneConnections(const std::vector<LaneInfo>& lanes, cv::Mat& canvas) const {
     122           0 :         for (size_t i = 0; i < lanes.size(); ++i) {
     123           0 :                 for (size_t j = i + 1; j < lanes.size(); ++j) {
     124           0 :                         if (shouldMerge(lanes[i], lanes[j])) {
     125           0 :                                 auto [a1, a2] = getExtremities(lanes[i].contour);
     126           0 :                                 auto [b1, b2] = getExtremities(lanes[j].contour);
     127             : 
     128             :                                 std::vector<std::pair<cv::Point, cv::Point>> pairs = {
     129             :                                         {a1, b1}, {a1, b2}, {a2, b1}, {a2, b2}
     130           0 :                                 };
     131             : 
     132           0 :                                 auto bestPair = *std::min_element(pairs.begin(), pairs.end(), [](const auto& p1, const auto& p2) {
     133           0 :                                         return cv::norm(p1.first - p1.second) < cv::norm(p2.first - p2.second);
     134           0 :                                 });
     135             : 
     136           0 :                                 cv::line(canvas, bestPair.first, bestPair.second, cv::Scalar(255, 255, 255), 3);
     137             :                         }
     138             :                 }
     139             :         }
     140           0 : }
     141             : 
     142           0 : cv::Mat LanePostProcessor::renderFilteredMask(const std::vector<LaneInfo>& lanes, cv::Size shape) const {
     143           0 :         cv::Mat canvas(shape, CV_8UC3, cv::Scalar(0, 0, 0));
     144           0 :         for (const auto& lane : lanes) {
     145           0 :                 cv::drawContours(canvas, std::vector<std::vector<cv::Point>>{lane.contour}, -1, cv::Scalar(255, 255, 255), cv::FILLED);
     146             :         }
     147           0 :         return canvas;
     148             : }

Generated by: LCOV version 1.14