基于OpenCV的自动报靶识别实验

基于OpenCV的自动报靶识别实验

问题

户外胸环靶自动报靶问题,目前是通过声电等方式来识别,成本较高,本文尝试使用图像处理的方法来识别。
Alt

方案

前提:固定相机

  1. 确定靶子的四个顶点:目前使用人工手动标注,暂不考虑自动,这步尤为关键直接影响后续弹孔位置确定
  2. 提取靶子:透视变换
  3. 寻找最新的弹孔:a)优先图像处理 + 帧差,需要像素级对齐,b)深度学习检测器,需要收集训练数据,对粘连弹孔检测效果未必理想
  4. 寻找同心圆圆心:椭圆检测方法,https://github.com/memory-overflow/standard-ellipse-detection
  5. 依据圆心与弹孔的距离确定环数

实验

  1. 手动确定靶子四个顶点:
    坐标如下:
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}};
  1. 提取靶子
    利用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));

效果:
Alt

  1. 寻找最新弹孔
    先进行灰度化 -> 进行中值滤波减少噪声干扰 -> 二值化,弹孔明显黑于其它
	// 灰度
    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);

二值化效果:
Alt
帧差结果,仍然有些对齐误差或者图像退化导致的像素差异:

cv::Mat diff = bin2 - bin1;

效果:
Alt
使用中值滤波去除误差影响,图像腐蚀膨胀也可以

    cv::medianBlur(diff, diff, 3); // 中值滤波滤除残差像素

效果:
Alt
寻找弹孔轮廓,计算图像中心矩确定弹孔中心点:

    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);
    }

效果:
Alt
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);

    }

效果:
Alt

结论

虽然可以通过图像处理的方法,确定弹孔和圆心的位置,从而换算出靶数;户外打靶很受环境因素影响,一套参数很难适用全部情况,且提取的同心圆有畸变影响靶数的准确,而且靶子的顶点自动定位是个较大的困难,如果相机视角再是不固定的情况,还得做像素级配准。

猜你喜欢

转载自blog.csdn.net/hqq091425/article/details/131134534