205 lines
7.7 KiB
C++
205 lines
7.7 KiB
C++
/*
|
||
* @Descripttion:
|
||
* @version:
|
||
* @Author: tjk
|
||
* @Date: 2023-01-31 17:01:02
|
||
* @LastEditors: Please set LastEditors
|
||
* @LastEditTime: 2023-02-01 17:04:12
|
||
*/
|
||
|
||
#include <iostream>
|
||
#include <string>
|
||
#include <opencv2/opencv.hpp>
|
||
|
||
using namespace std;
|
||
|
||
//定义击打板点中心、距离
|
||
typedef struct armor_point {
|
||
cv::Point point;
|
||
double dis;
|
||
}armor_point;
|
||
|
||
//画出旋转矩形
|
||
void drawRotatedRect(cv::Mat& img, const cv::RotatedRect& rect, const cv::Scalar& color, int thickness)
|
||
{
|
||
cv::Point2f Vertex[4];
|
||
rect.points(Vertex);
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
cv::line(img, Vertex[i], Vertex[(i + 1) % 4], color, thickness);
|
||
}
|
||
}
|
||
double distance(cv::Point a, cv::Point b)
|
||
{
|
||
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
|
||
}
|
||
|
||
|
||
int main()
|
||
{
|
||
//读取视频资源
|
||
string video_path ="./vedio/blue2.mp4";
|
||
cv::VideoCapture cap;
|
||
cap.open(video_path);
|
||
|
||
|
||
// cv::VideoWriter writer;
|
||
// int coder = cv::VideoWriter::fourcc('M','J','P','G');//选择编码格式
|
||
// double fps=25.0;//设置视频帧率
|
||
// string filename="live.avi";//保存的视频文件名称
|
||
// writer.open(filename,coder,fps,cv::Size(1350,1080),true);//创建保存视频文件的视频流
|
||
// if(!writer.isOpened()){
|
||
// cout<<"打开视频文件失败,请确认是否为合法输入"<<endl;
|
||
// return -1;
|
||
// }
|
||
|
||
|
||
while (true) {
|
||
cv::Mat src_frame;
|
||
cap >> src_frame;
|
||
cv::resize(src_frame, src_frame, cv::Size(640, 480));
|
||
//读取视频、重新规定窗口大小
|
||
|
||
vector<cv::Mat> bgr_images;
|
||
cv::split(src_frame, bgr_images);
|
||
cv::Mat b_src_img = bgr_images[0];
|
||
//分离颜色通道、取蓝色通道进行图像模糊
|
||
cv::blur(b_src_img, b_src_img, cv::Size(3, 3));
|
||
//二值化
|
||
cv::Mat threshold_img;
|
||
cv::threshold(b_src_img, threshold_img, 130, 255, cv::THRESH_BINARY);
|
||
//寻找轮廓
|
||
vector<vector<cv::Point>> contours;
|
||
cv::findContours(threshold_img, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
||
cv::drawContours(src_frame,contours,-1,cv::Scalar(0,0,255));
|
||
|
||
cv::Point r_center; // R 的中心点
|
||
vector<cv::RotatedRect> contours_min_rects;//所有轮廓的最小外接矩形
|
||
vector<cv::RotatedRect> armor_min_rects;
|
||
vector<cv::RotatedRect> target_min_rects;
|
||
vector<cv::RotatedRect> r_min_rects; //R
|
||
|
||
int r_index = -1;
|
||
// int cnt = 0;
|
||
for (unsigned int contour_index = 0; contour_index < contours.size(); contour_index++) {
|
||
// //寻找最小旋转矩形,放进容器,顺便将面积过大的剔除,长宽比悬殊的剔除
|
||
cv::RotatedRect minrect = minAreaRect(contours[contour_index]);
|
||
cv::Rect rect = boundingRect(contours[contour_index]);
|
||
|
||
//找到旋转矩形
|
||
if (minrect.size.area() <= 6000.0 && minrect.size.area() > 150) {
|
||
float width;
|
||
float height;
|
||
//判断长宽比 宽长高短
|
||
if (minrect.size.width > minrect.size.height) {
|
||
width = minrect.size.width;
|
||
height = minrect.size.height;
|
||
}
|
||
else {
|
||
width = minrect.size.height;
|
||
height = minrect.size.width;
|
||
}
|
||
//如果宽高比小于5 ,再判断是不是中心R标 如果宽高比小于1.17 那么则是中心R标 注意此处中心R标的限定条件有中心的位置!
|
||
if (width / height < 5) {
|
||
contours_min_rects.push_back(minrect);
|
||
if (minrect.size.area() > 200 && minrect.size.area() < 350 && minrect.center.y > 100) { // find R
|
||
if (height / width > 0.85) {
|
||
// R_minRects.push_back(minrect);
|
||
r_center = minrect.center;
|
||
cv::circle(src_frame, minrect.center, 15, cv::Scalar(5, 255, 100));
|
||
r_index = contour_index;
|
||
// std::cout<<cnt++<<std::endl;
|
||
}
|
||
}
|
||
//如果面积不满足要求并且宽高比小于1.42 那么为最小旋转矩形
|
||
else {
|
||
if (minrect.size.area() > 300 && minrect.size.area() < 4350 && (height / width) < 0.7) {
|
||
armor_min_rects.push_back(minrect);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
bool find_ok = false;
|
||
|
||
//计算两个最小旋转矩形之间的距离
|
||
for (int i = 0; i < armor_min_rects.size() - 1; i++) {
|
||
for (int j = i + 1; j < armor_min_rects.size(); j++) {
|
||
double dis = distance(armor_min_rects[i].center, armor_min_rects[j].center);
|
||
//如果距离小于100 那么将其视为可能的击打板
|
||
if (dis < 100) {
|
||
target_min_rects.push_back(armor_min_rects[i]);
|
||
target_min_rects.push_back(armor_min_rects[j]);
|
||
find_ok = true;
|
||
break;
|
||
}
|
||
// std::cout<<dis<<std::endl;
|
||
}
|
||
if (find_ok) {
|
||
break;
|
||
}
|
||
}
|
||
//如果没有找到两个对应的最小矩形 那么重新进入while循环
|
||
if (target_min_rects.size() != 2) {
|
||
continue;
|
||
}
|
||
else {
|
||
//找到两个最小矩形对应的点 由此找到中心点
|
||
cv::RotatedRect rrect_in; //= target_minRects[0];
|
||
cv::RotatedRect rrect_out;// = target_minRects[1];
|
||
double dis1 = distance(r_center, target_min_rects[0].center);
|
||
double dis2 = distance(r_center, target_min_rects[1].center);
|
||
if (dis1 > dis2) {
|
||
rrect_in = target_min_rects[1];
|
||
rrect_out = target_min_rects[0];
|
||
}
|
||
else {
|
||
rrect_in = target_min_rects[0];
|
||
rrect_out = target_min_rects[1];
|
||
}
|
||
drawRotatedRect(src_frame, rrect_in, cv::Scalar(0, 250, 0), 1);
|
||
drawRotatedRect(src_frame, rrect_out, cv::Scalar(0, 0, 255), 1);
|
||
|
||
cv::Point target_center = cv::Point((int)((rrect_in.center.x + rrect_out.center.x) / 2), (int)((rrect_in.center.y + rrect_out.center.y) / 2));
|
||
cv::circle(src_frame, target_center, 5, cv::Scalar(0, 0, 255), -1);
|
||
cv::Point2f in_vet_points[4];
|
||
cv::Point2f out_vet_points[4];
|
||
//找到内外两个矩形的所有点 计算出所有点与中心的距离
|
||
rrect_in.points(in_vet_points);
|
||
rrect_out.points(out_vet_points);
|
||
vector<armor_point> armor_points;
|
||
for (int i = 0; i < 4; i++) {
|
||
armor_point point;
|
||
point.point = in_vet_points[i];
|
||
point.dis = distance(target_center, in_vet_points[i]);
|
||
armor_points.push_back(point);
|
||
}
|
||
for (int i = 0; i < 4; i++) {
|
||
armor_point point;
|
||
point.point = out_vet_points[i];
|
||
point.dis = distance(target_center, out_vet_points[i]);
|
||
armor_points.push_back(point);
|
||
}
|
||
//找到所有点与中心距离最近的四个点
|
||
sort(armor_points.begin(), armor_points.end(), [](armor_point a, armor_point b) {return a.dis < b.dis; });
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
cv::circle(src_frame, armor_points[i].point, 3, cv::Scalar(255, 0, 255), -1);
|
||
}
|
||
int buff_run_radius = (int)distance(r_center, target_center);
|
||
cv::circle(src_frame, r_center, buff_run_radius, cv::Scalar(55, 110, 255), 1);
|
||
}
|
||
// cv::resize(src_frame,src_frame,cv::Size(1350,1080));
|
||
// writer.write(src_frame);//把图像写入视频流
|
||
cv::imshow("src_img", src_frame);
|
||
cv::imshow("threshold_img",threshold_img);
|
||
if (cv::waitKey(0) == 'q') {
|
||
break;
|
||
}
|
||
|
||
}
|
||
// writer.release();
|
||
cap.release();
|
||
return 0;
|
||
}
|