distance_pr的算子很快
使用opencv模仿实现一下
halcon 的 region使用rle编码,还有可能使用凸包优化,simd,二分查找,多线程计算,这里只实现基础的功能
#include <opencv2/opencv.hpp>
#include <vector>
#include <limits>
#include <algorithm>
// 结构体表示RLE编码的区域点
struct RLEPoint {
int y;
int x_start;
int x_end;
};
// 从二值图像生成RLE编码的区域表示
std::vector<RLEPoint> binaryImageToRLE(const cv::Mat& binImage) {
std::vector<RLEPoint> rlePoints;
CV_Assert(binImage.type() == CV_8UC1);
for (int y = 0; y < binImage.rows; ++y) {
const uchar* row = binImage.ptr<uchar>(y);
bool inRun = false;
int runStart = 0;
for (int x = 0; x < binImage.cols; ++x) {
if (row[x] > 0) { // 前景像素
if (!inRun) {
inRun = true;
runStart = x;
}
} else {
if (inRun) {
inRun = false;
rlePoints.push_back({y, runStart, x-1});
}
}
}
if (inRun) {
rlePoints.push_back({y, runStart, binImage.cols-1});
}
}
return rlePoints;
}
// 计算凸包点集
std::vector<cv::Point> computeConvexHull(const std::vector<RLEPoint>& rlePoints) {
std::vector<cv::Point> allPoints;
// 将RLE点转换为离散点
for (const auto& rle : rlePoints) {
for (int x = rle.x_start; x <= rle.x_end; ++x) {
allPoints.emplace_back(x, rle.y);
}
}
// 计算凸包
std::vector<cv::Point> hull;
cv::convexHull(allPoints, hull);
return hull;
}
// 射线法判断点是否在凸包内
bool isPointInConvexHull(const cv::Point& p, const std::vector<cv::Point>& hull) {
if (hull.size() < 3) return false;
// 检查点是否在所有边的同一侧
int prevSide = 0;
for (size_t i = 0; i < hull.size(); ++i) {
const cv::Point& p1 = hull[i];
const cv::Point& p2 = hull[(i+1)%hull.size()];
// 计算叉积
int cross = (p2.x - p1.x) * (p.y - p1.y) - (p2.y - p1.y) * (p.x - p1.x);
if (cross == 0) continue; // 在边上
int currentSide = cross > 0 ? 1 : -1;
if (prevSide == 0) {
prevSide = currentSide;
} else if (currentSide != prevSide) {
return false;
}
}
return true;
}
// 优化的distance_pr实现
double optimized_distance_pr(const cv::Mat& region, const cv::Point& queryPoint) {
// 1. 转换为RLE编码表示
auto rlePoints = binaryImageToRLE(region);
if (rlePoints.empty()) return std::numeric_limits<double>::max();
// 2. 计算凸包
auto convexHull = computeConvexHull(rlePoints);
// 3. 检查查询点是否在凸包内
bool inside = isPointInConvexHull(queryPoint, convexHull);
// 4. 计算最小距离
double minDist = std::numeric_limits<double>::max();
if (inside) {
// 如果在凸包内,距离可能为0,需要检查精确包含关系
for (const auto& rle : rlePoints) {
if (rle.y == queryPoint.y) {
if (queryPoint.x >= rle.x_start && queryPoint.x <= rle.x_end) {
return 0.0; // 点在区域内
}
}
}
}
// 5. 使用凸包顶点快速估算搜索范围
double searchRadius = std::numeric_limits<double>::max();
for (const auto& p : convexHull) {
double dx = p.x - queryPoint.x;
double dy = p.y - queryPoint.y;
double dist = std::sqrt(dx*dx + dy*dy);
if (dist < searchRadius) {
searchRadius = dist;
}
}
// 6. 在RLE点集中搜索最小距离
for (const auto& rle : rlePoints) {
// 快速检查是否可能在搜索范围内
if (std::abs(rle.y - queryPoint.y) > searchRadius) continue;
// 计算到该行的最小距离
int closestX = std::max(rle.x_start, std::min(rle.x_end, queryPoint.x));
double dx = closestX - queryPoint.x;
double dy = rle.y - queryPoint.y;
double distance = std::sqrt(dx*dx + dy*dy);
if (distance < minDist) {
minDist = distance;
// 更新搜索半径
if (distance < searchRadius) {
searchRadius = distance;
}
}
}
return minDist;
}