基于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

结论

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

识别旋转的子,可以使用以下步骤: 1. 对子进行边缘检测,得到子的边缘。 2. 使用霍夫变换检测子的圆心和半径。 3. 将子旋转至水平方向。 4. 检测子标志点的位置。 5. 计算子的旋转角度。 下面是示例代码: ``` import cv2 import numpy as np # 读取图像 img = cv2.imread('target.jpg') gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 边缘检测 edges = cv2.Canny(gray, 50, 150, apertureSize=3) # 霍夫变换检测圆 circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0) # 获取圆心和半径 circles = np.uint16(np.around(circles)) circle = circles[0][0] center = (circle[0], circle[1]) radius = circle[2] # 将子旋转至水平方向 h, w = img.shape[:2] M = cv2.getRotationMatrix2D(center, -90, 1) img = cv2.warpAffine(img, M, (w, h)) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 边缘检测 edges = cv2.Canny(gray, 50, 150, apertureSize=3) # 霍夫变换检测标志点 lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100, minLineLength=50, maxLineGap=10) lines = lines.squeeze(1) x_vals = lines[:, [0, 2]].reshape(-1) y_vals = lines[:, [1, 3]].reshape(-1) x1,y1 = min(x_vals), min(y_vals) x2,y2 = max(x_vals), max(y_vals) pt1, pt2 = (x1, y1), (x2, y2) cv2.line(img, pt1, pt2, (0, 0, 255), 2) # 计算旋转角度 theta = np.arctan2(pt2[1] - pt1[1], pt2[0] - pt1[0]) theta = np.rad2deg(theta) if theta < 0: theta += 180 print('旋转角度:', theta) # 显示结果 cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 其中,`target.jpg`为子图像文件名。运行以上代码将得到旋转后的子图像和旋转角度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上单之光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值