户外胸环靶自动报靶问题,目前是通过声电等方式来识别,成本较高,本文尝试使用图像处理的方法来识别。
前提:固定相机
const std::vector<cv::Point2f> src_points = {{241, 0}, {417, 2145}, {3325, 0}, {3209, 2157}};
const std::vector<cv::Point2f> dst_points = {{0, 0}, {0, 500}, {500, 0}, {500, 500}};
cv::Mat M = cv::getPerspectiveTransform(src_points, dst_points);
cv::Mat target1, target2;
cv::warpPerspective(img1, target1, M, cv::Size(500, 500));
cv::warpPerspective(img2, target2, M, cv::Size(500, 500));
效果:
// 灰度
cv::Mat gray1_, gray2_;
cv::cvtColor(target1, gray1_, cv::COLOR_BGR2GRAY);
cv::cvtColor(target2, gray2_, cv::COLOR_BGR2GRAY);
// 中值滤波
cv::Mat gray1, gray2;
cv::medianBlur(gray1_, gray1, 5);
cv::medianBlur(gray2_, gray2, 5);
// 二值化
cv::Mat bin1, bin2;
cv::threshold(gray1, bin1, 90, 255, cv::THRESH_BINARY_INV);
cv::threshold(gray2, bin2, 90, 255, cv::THRESH_BINARY_INV);
二值化效果:
帧差结果,仍然有些对齐误差或者图像退化导致的像素差异:
cv::Mat diff = bin2 - bin1;
cv::medianBlur(diff, diff, 3); // 中值滤波滤除残差像素
cv::findContours(diff, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
//计算图像矩
std::vector<cv::Moments> mu(contours.size());
std::vector<cv::Point2f> mc(contours.size());
for (int i = 0; i < contours.size(); i++) {
mu[i] = cv::moments(contours[i], false);
mc[i] = cv::Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
cv::circle(diff, mc[i], 1, cv::Scalar(0), -1);
}
效果:
4. 寻找同心圆圆心:
椭圆检测方法,https://github.com/memory-overflow/standard-ellipse-detection
std::vector<std::shared_ptr<Ellipse> > ells;
bool success = detectEllipse(gray1_.data, gray1_.rows, gray1_.cols, ells);
std::cout << ells.size() << std::endl;
for (const auto& ell : ells) {
std::cout << "coverage: " << ell->coverangle << ", goodness: " << ell->goodness << ", polarity: " << ell->polarity << std::endl;
cv::ellipse(target1,
cv::Point(ell->o.y, ell->o.x),
cv::Size(ell->a, ell->b),
rad2angle(PI_2 - ell->phi),
0,
360,
cv::Scalar(0, 255, 0), 2, 8);
}
虽然可以通过图像处理的方法,确定弹孔和圆心的位置,从而换算出靶数;户外打靶很受环境因素影响,一套参数很难适用全部情况,且提取的同心圆有畸变影响靶数的准确,而且靶子的顶点自动定位是个较大的困难,如果相机视角再是不固定的情况,还得做像素级配准。