60 int nNeighborhoodSize = 5,
61 float fDepthPercentileTarget = 0.20f,
62 float fDepthToleranceMeters = 0.5f)
65 if (nNeighborhoodSize < 1)
67 LOG_WARNING(logging::g_qSharedLogger,
"GeolocateBox: Invalid neighborhood size {}, defaulting to 5x5", nNeighborhoodSize);
68 nNeighborhoodSize = 5;
75 int nHalfSize = nNeighborhoodSize / 2;
78 std::vector<cv::Vec4f> vRawPoints;
79 std::vector<float> vRawZ;
82 for (
int nOffsetY = -nHalfSize; nOffsetY <= nHalfSize; ++nOffsetY)
84 for (
int nOffsetX = -nHalfSize; nOffsetX <= nHalfSize; ++nOffsetX)
87 int nCurrY = cvPixel.
y + nOffsetY;
88 int nCurrX = cvPixel.
x + nOffsetX;
91 if (nCurrY < 0 || nCurrY >= cvPointcloud.
rows || nCurrX < 0 || nCurrX >= cvPointcloud.
cols)
100 if (cvPoint[2] > 0 && std::isfinite(cvPoint[0]) && std::isfinite(cvPoint[1]) && std::isfinite(cvPoint[2]))
102 vRawPoints.push_back(cvPoint);
103 vRawZ.push_back(cvPoint[2]);
114 std::vector<float> vX, vY, vZ, vDistances;
119 LOG_DEBUG(logging::g_qSharedLogger,
"GeolocateBox: Stereovision failed for pixel ({}, {}). Attempting Monocular Fallback.", cvPixel.
x, cvPixel.
y);
123 int nBottomY = std::min(cvPointcloud.
rows - 1, cvPixel.
y + nHalfSize);
126 float fFy = cvPointcloud.
rows / 2.0f;
127 float fCy = cvPointcloud.
rows / 2.0f;
130 float fCameraHeight =
static_cast<float>(stCameraUTM.dAltitude);
132 float fRayAngleY =
atan2(nBottomY - fCy, fFy);
133 float fTotalAngle = fRayAngleY;
136 if (fTotalAngle <= 0.01f)
138 LOG_DEBUG(logging::g_qSharedLogger,
"GeolocateBox: Fallback failed. Object is on or above the horizon line.");
142 fAvgZ = fCameraHeight /
tan(fTotalAngle);
143 fAvgX = ((cvPixel.
x - (cvPointcloud.
cols / 2.0f)) / fFy) * fAvgZ;
144 fAvgY = -fCameraHeight;
150 std::sort(vRawZ.begin(), vRawZ.end());
151 size_t unPercentileIndex =
static_cast<size_t>(vRawZ.size() * fDepthPercentileTarget);
152 float fTargetZ = vRawZ[unPercentileIndex];
155 for (
const auto& cvPoint : vRawPoints)
157 if (std::abs(cvPoint[2] - fTargetZ) <= fDepthToleranceMeters)
159 vX.push_back(cvPoint[0]);
160 vY.push_back(cvPoint[1]);
161 vZ.push_back(cvPoint[2]);
167 fAvgX = std::accumulate(vX.begin(), vX.end(), 0.0f) / vX.size();
168 fAvgY = std::accumulate(vY.begin(), vY.end(), 0.0f) / vY.size();
169 fAvgZ = std::accumulate(vZ.begin(), vZ.end(), 0.0f) / vZ.size();
174 fAvgX = vRawPoints[unPercentileIndex][0];
175 fAvgY = vRawPoints[unPercentileIndex][1];
176 fAvgZ = vRawPoints[unPercentileIndex][2];
183 double dHeadingRad = dAdjustedHeading * M_PI / 180.0;
192 double dEasting = stCameraUTM.dEasting + (fAvgZ *
cos(dHeadingRad) + fAvgX *
sin(dHeadingRad));
193 double dNorthing = stCameraUTM.dNorthing + (fAvgZ *
sin(dHeadingRad) - fAvgX *
cos(dHeadingRad));
196 double dAltitude = stCameraUTM.dAltitude + fAvgY;
199 geoops::UTMCoordinate stObjectUTM(dEasting, dNorthing, stCameraUTM.nZone, stCameraUTM.bWithinNorthernHemisphere, dAltitude);
202 double dRadius = 0.0;
206 for (
size_t siI = 0; siI < vX.size(); ++siI)
208 double dX = vX[siI] - fAvgX;
209 double dY = vY[siI] - fAvgY;
210 double dZ = vZ[siI] - fAvgZ;
211 vDistances.push_back(
sqrt(dX * dX + dY * dY + dZ * dZ));
215 std::sort(vDistances.begin(), vDistances.end());
216 if (vDistances.size() % 2 == 0)
218 dRadius = (vDistances[vDistances.size() / 2 - 1] + vDistances[vDistances.size() / 2]) / 2.0;
222 dRadius = vDistances[vDistances.size() / 2];
230 return geoops::Waypoint(stObjectUTM, geoops::WaypointType::eUNKNOWN, dRadius);