问题
户外胸环靶自动报靶问题,目前是通过声电等方式来识别,成本较高,本文尝试使用图像处理的方法来识别。
方案
前提:固定相机
- 确定靶子的四个顶点:目前使用人工手动标注,暂不考虑自动,这步尤为关键直接影响后续弹孔位置确定
- 提取靶子:透视变换
- 寻找最新的弹孔:a)优先图像处理 + 帧差,需要像素级对齐,b)深度学习检测器,需要收集训练数据,对粘连弹孔检测效果未必理想
- 寻找同心圆圆心:椭圆检测方法,https://github.com/memory-overflow/standard-ellipse-detection
- 依据圆心与弹孔的距离确定环数
实验
- 手动确定靶子四个顶点:
坐标如下:
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}};
- 提取靶子
利用opencv提供的透视变换
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);
}
效果:
文章来源:https://www.toymoban.com/news/detail-478980.html
结论
虽然可以通过图像处理的方法,确定弹孔和圆心的位置,从而换算出靶数;户外打靶很受环境因素影响,一套参数很难适用全部情况,且提取的同心圆有畸变影响靶数的准确,而且靶子的顶点自动定位是个较大的困难,如果相机视角再是不固定的情况,还得做像素级配准。文章来源地址https://www.toymoban.com/news/detail-478980.html
到了这里,关于基于OpenCV的自动报靶识别实验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!