<模板> 矩形分割

描述

N个不同的颜色的不透明的长方形(1 <= N <= 1000)被放置在一张横宽为A竖长为B的白纸上。这些长方形被放置时,保证了它们的边与白纸的边缘平行。所有的长方形都放置在白纸内,所以我们会看到不同形状的各种颜色。坐标系统的原点(0,0)设在这张白纸的左下角,而坐标轴则平行于边缘。

[编辑]格式

PROGRAM NAME: rect1

INPUT FORMAT:

(file rect1.in)

按顺序输入放置长方形的方法。第一行输入的是那个放在底的长方形(即白纸)。

第 1 行: A , B 和 N由空格分开 (1 <=A, B<=10,000)

第 2 到N+1行: 为五个整数 llx, lly, urx, ury, color 这是一个长方形的左下角坐标,右上角坐标(x+1,y+1)和颜色。

颜色 1和底部白纸的颜色相同。 (1 <= color <= 2500)

OUTPUT FORMAT

(file rect1.out)

输出且仅输出所有能被看到颜色,和该颜色的总面积(可以由若干个不连通的色块组成),按color增序排列。

[编辑]SAMPLE INPUT

20 20 3
2 2 18 18 2
0 8 19 19 3
8 0 10 19 4

[编辑]SAMPLE OUTPUT

1 91
2 84
3 187
4 38

矩形集合中已有矩形(x1,y1,x2,y2),现加入矩形(x3,y3,x4,y4)。它们的位置关系可以有很多种(有17种之多),这里就不一一列举了。但无论它们的位置关系如何复杂,运用线段切割的思想来进行矩形切割,就会变得十分明了。我们将矩形的切割正交分解,先进行x方向上的切割,再进行y方向的切割。如下图所示:矩形切割

插入矩形(x3,y3,x4,y4)后,对矩形(x1,y1,x2,y2)进行切割。

  • Step 1:首先从x方向上切。把线段(x1,x2)切成(x1,x3),(x4,x2)两条线段。于是相应地,我们就把两个矩形切了出来——(x1,y1,x3,y2),(x4,y1,x2,y2)。把它们加到矩形集合中。去掉了这两个矩形后,我们要切的矩形就变为(x3,y1,x4,y2)。
  • Step 2:接着我们再进行y方向上的切割。把线段(y1,y2)切成(y1,y3)。相应地又得到一个矩形(x3,y1,x4,y2)。把它放入矩形集合。
  • Step 3:剩下的矩形为(x3,y3,x4,y2),这个矩形已经被矩形(x3,y3,x4,y4)覆盖了,因此直接把它删掉。

我们可以归纳出矩形切割的思想:

  • 1、先对被切割矩形进行x方向上的切割。取(x1,x2),(x3,x4)的交集(k1,k2)

            ①若x1<k1,则加入矩形(x1,y1,k1,y2)

            ②若k2<x2,则加入矩形(k2,y1,x2,y2)

  • 2、再对切剩的矩形(k1,y1,k2,y2) 进行y 方向上的切割。取(y1,y2),(y3,y4)的交集(k3,k4)

            ① 若y1<k3,则加入矩形(k1,y1,k2,k3)

            ②若k4<y2,则加入矩形(k1,k4,k2,y2)

  • 3、把矩形(x1,y1,x2,y2)从矩形集合中删除。
Procedure Cut(x1,y1,x2,y2,Direction)
Var k1,k2
    Begin
        Case Direction of
        1:Begin
            k1 ← Max(x1,x3) {计算线段(x1,x2),(x3,x4)交集的左边界}
            k2 ← Min(x2,x4) {计算线段(x1,x2),(x3,x4)交集的右边界}
            if x1<k1 then Add(x1,y1,k1,y2)
            if k2<x2 then Add(k2,y1,x2,y2)
            Cut(k1,y1,k2,y2,Direction+1)
        End
        2:Begin
            k1 ← Max(y1,y3)
            k2 ← Min(y2,y4)
            if y1<k1 then Add(x1,y1,x2,k1)
            if k2<y2 then Add(x1,k2,x2,y2)
        End
    End
End
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <map>
#include <stack>
#include <list>
#include <vector>
#define LL __int64
using namespace std;
struct node
{
	int lx,ly,rx,ry,cl;
}f[1010];
int ans[10010];
void DFS(int lx,int ly,int rx,int ry,int t)
{
	if (t==0) return;
	if (f[t].lx>=rx || f[t].rx<=lx || f[t].ly>=ry || f[t].ry<=ly )
		DFS(lx,ly,rx,ry,t-1);
	else
	{
		int k1=max(lx,f[t].lx);
		int k2=min(rx,f[t].rx);
		if (lx<k1)
			DFS(lx,ly,k1,ry,t-1);
		if (rx>k2)
			DFS(k2,ly,rx,ry,t-1);
		int k3=max(ly,f[t].ly);
		int k4=min(ry,f[t].ry);
		if (ly<k3)
			DFS(k1,ly,k2,k3,t-1);
		if (ry>k4)
			DFS(k1,k4,k2,ry,t-1);
		ans[f[t].cl]+=abs(k2-k1)*abs(k4-k3);
		ans[1]-=abs(k2-k1)*abs(k4-k3);
	}
}
int main()
{
	int n,m,k,i;
	scanf("%d%d%d",&n,&m,&k);
	for (i=1;i<=k;i++)
		scanf("%d%d%d%d%d",&f[i].lx,&f[i].ly,&f[i].rx,&f[i].ry,&f[i].cl);
	memset(ans,0,sizeof(ans));
	ans[1]=n*m;
	DFS(0,0,n,m,k);
	for (i=1;i<=n;i++)
		if (ans[i])
			printf("%d %d\n",i,ans[i]);
	return 0;
}



armor_detector.hpp #ifndef ARMOR_DETECTOR_H #define ARMOR_DETECTOR_H #include<iostream> #include<opencv2/opencv.hpp> #include<opencv2/dnn.hpp> #include<vector> using namespace std ; using namespace cv ; struct LightBar { RotatedRect rect; double area; double angle; }; struct ArmorPlate { vector<Point2f>corners;// 装甲板四个角点坐标 Point2f center; Size2f size; double angle; }; class ArmorDetector { public: ArmorDetector(); bool processFrame(const Mat&frame);// 处理单帧图像,返回是否检测到装甲板 void drawResult(const Mat&output); private: Mat preprocessFrame(const Mat&frame);// 获取预处理后的图像 vector<LightBar>findLightBars(const Mat&binary);// 在二值图中找灯条// 在预处理图像上找灯条 vector<ArmorPlate>pairLightBars(const vector<LightBar>&lightbars);// 配对灯条成装甲板 // HSV颜色范围参数(用于识别特定颜色的灯条) int h_min = 100, h_max = 130; // 色调范围 int s_min = 100, s_max = 255; // 饱和度范围 int v_min = 100, v_max = 255; // 亮度范围 vector<ArmorPlate>detectedArmors;// 存储检测到的装甲板 }; #endif armor_detector.cpp #include "armor_detector.hpp" #include<iostream> #include<cmath> #include<vector> #include<opencv2/opencv.hpp> using namespace std ; using namespace cv; ArmorDetector::ArmorDetector() { h_min = 100; h_max = 130; // 色调范围 s_min = 100; s_max = 255; // 饱和度范围 v_min = 100; v_max = 255; // 亮度范围 }; Mat ArmorDetector::preprocessFrame(const Mat&frame) { if(frame.empty()) { cout<<"frame faile"<<endl; return Mat(); } Mat hsv,mask; // 转换为HSV颜色空间(更适合颜色分割) cvtColor(frame,hsv,COLOR_BGR2HSV); Scalar lower_blue(h_min,s_min,v_min); Scalar upper_blue(h_max,s_max,v_max); //隐式类型推导​:inRange()函数自动将输出设置为CV_8UC1类型,mask为二值图像 inRange(hsv,lower_blue,upper_blue,mask); Mat kernel=getStructuringElement(MORPH_RECT,Size(5,5)); morphologyEx(mask,mask,MORPH_OPEN,kernel); morphologyEx(mask,mask,MORPH_CLOSE,kernel); return mask; } // bool ArmorDetector::processFrame(const Mat&frame) // { // Mat binary=preprocessFrame(frame); // vector<LightBar>lightBars=findLightBars(binary); // detectedArmors=pairLightBars(lightBars); // return !detectedArmors.empty(); // } vector<LightBar>ArmorDetector::findLightBars(const Mat&binary) { vector<vector<Point>>contours; vector<LightBar>lightBars; if(binary.empty()) { cout<<"binary faile"<<endl; return lightBars; } // 查找二值图像中的轮廓 findContours(binary,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE); for(const auto &contour:contours) { double area=contourArea(contour); // 面积过滤(去除太小的噪声) if(area<100.0) { continue; } // 获取最小外接旋转矩形 RotatedRect rect=minAreaRect(contour); float width=rect.size.width; float height=rect.size.height; // 计算长宽比(灯条通常是细长的) float radio=(width>height)?width/height:height/width; // 筛选灯条条件:长宽比>2.0且面积>50 //vector<LightBar>lightBars; if(radio>2.0&&area>50.0) { LightBar lightBar; lightBar.rect=rect; lightBar.area=area; lightBar.angle=rect.angle; lightBars.push_back(lightBar); } } return lightBars; } vector<ArmorPlate>ArmorDetector::pairLightBars(const vector<LightBar>&lightBars) { vector<ArmorPlate>armors; if(lightBars.size()<2)// 至少需要2个灯条 { return armors; } // 遍历所有灯条组合 for(size_t i=0;i<lightBars.size();++i) { for(size_t j=i+1;j<lightBars.size();++j) { const LightBar& bar1=lightBars[i]; const LightBar& bar2=lightBars[j]; // 计算两个灯条中心的距离 Point2f center1=bar1.rect.center; Point2f center2=bar2.rect.center; float distance=norm(center1-center2); // 计算角度差(归一化到0-90度) float angleDiff=(bar1.angle>bar2.angle)?(bar1.angle-bar2.angle):(bar2.angle-bar1.angle); if(angleDiff>90.0) { angleDiff=180.0-angleDiff; } float height1=bar1.rect.size.height; float height2=bar2.rect.size.height; float heightDiff=(height1>height2)?(height1-height2):(height2-height1); //配对条件 像素单位 if(distance>50.0&&distance<300.0&&angleDiff<20.0&&heightDiff<30.0) { ArmorPlate armor; armor.center=(center1+center2)*0.5f; // 计算装甲板大小 float armorWidth=distance*1.5f; float armorHeight=(height1+height2)*0.8f; armor.size=Size2f(armorWidth,armorHeight); // 计算装甲板角度(取两个灯条角度的平均值) armor.angle=(bar1.angle+bar2.angle)*0.5f; // // 计算四个角点 // // Point2f pts[4]; // // bar1.rect.points(pts); // 这会覆盖bar1的角点到pts // // bar2.rect.points(pts); // 这又会覆盖bar2的角点到同一个pts // // armor.corners={corner1,corner2,Point2f()} // 基于装甲板矩形计算四个角点 RotatedRect armorRect(armor.center,armor.size,armor.angle); Point2f corners[4]; armorRect.points(corners);//将四个角点坐标填入数组(角点顺序:逆时针) //​将角点数组转换为向量:vector<Point2f>(开始指针, 结束指针):范围构造函数 armor.corners=vector<Point2f>(corners,corners+4); armors.push_back(armor); } } } return armors; } bool ArmorDetector::processFrame(const Mat&frame) { if(frame.empty()) { cout<<"frame faile"<<endl; return false; } Mat binary=preprocessFrame(frame); if(binary.empty()) { cout<<"binary faile"<<endl; return false; } vector<LightBar>lightBars=findLightBars(binary); detectedArmors=pairLightBars(lightBars); return !detectedArmors.empty(); } void ArmorDetector::drawResult(const Mat&output) { for (const auto& armor :detectedArmors) { // if (armor.size.area() < 100) // { // continue; // 过滤太小矩形 // } if(armor.corners.size()==4) {// 使用更粗的线条,减少绘制调用 for (int j = 0; j < 4; j++) { line(output, armor.corners[j], armor.corners[(j + 1) % 4], Scalar(0, 0, 255), 2, 8,0); } } } } mian.cpp #include"armor_detector.hpp" #include<opencv2/opencv.hpp> #include<iostream> using namespace std ; using namespace cv ; int main() { ArmorDetector detector; VideoCapture capture("1.mp4"); if(!capture.isOpened()) { return -1; } // 设置视频参数 capture.set(CAP_PROP_FRAME_WIDTH, 640); capture.set(CAP_PROP_FRAME_HEIGHT, 480); while(1) { Mat frame; capture>>frame; // 缩小图像提高处理速度 // Mat smallFrame; // resize(frame, smallFrame, Size(640, 480)); if (detector.processFrame(frame)) { detector.drawResult(frame); imshow("BlueArmor Detection", frame); } if(waitKey(30)>=0) { break; } } return 0; }为什么视频会卡顿,画的矩形会变竖直
11-07
#include <opencv2/opencv.hpp> #include <iostream> #include <filesystem> #include <map> #include <stack> #include <string> #include <cctype> #include<cmath>// using namespace std; using namespace cv; namespace fs = std::filesystem; // ============================================================ // Step 1. 加载模板 // ============================================================ map<string, Mat> loadTemplates(const string& templateDir) { map<string, Mat> templates; for (const auto& entry : fs::directory_iterator(templateDir)) { if (!entry.is_regular_file()) continue; string ext = entry.path().extension().string(); if (ext != ".png" && ext != ".jpg" && ext != ".jpeg" && ext != ".bmp" && ext != ".tif") continue; string name = entry.path().stem().string(); Mat t_img = imread(entry.path().string(), IMREAD_GRAYSCALE); if (t_img.empty()) continue; Mat t_bin; threshold(t_img, t_bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); templates[name] = t_bin; } cout << "加载模板数量: " << templates.size() << endl; return templates; } // ============================================================ // Step 2. 模板匹配 // ============================================================ pair<string, double> templateMatch(const Mat& symbol, const map<string, Mat>& templates) { string bestName = "未知"; double bestScore = 0.0; for (const auto& kv : templates) { Mat resized; resize(kv.second, resized, Size(symbol.cols, symbol.rows)); Mat res; matchTemplate(symbol, resized, res, TM_CCOEFF_NORMED); double minV, maxV; minMaxLoc(res, &minV, &maxV); if (maxV > bestScore) { bestScore = maxV; bestName = kv.first; } } return { bestName, bestScore }; } // ============================================================ // Step 3. x方向重叠 → 重新计算最小外接矩形 // ============================================================ vector<Rect> mergeByXOverlap(const vector<Rect>& boxes) { if (boxes.empty()) return {}; vector<Rect> sortedBoxes = boxes; sort(sortedBoxes.begin(), sortedBoxes.end(), [](const Rect& a, const Rect& b) { return a.x < b.x; }); vector<Rect> merged; vector<Rect> currentGroup; currentGroup.push_back(sortedBoxes[0]); for (size_t i = 1; i < sortedBoxes.size(); ++i) { Rect prev = currentGroup.back(); Rect curr = sortedBoxes[i]; int overlapX = min(prev.x + prev.width, curr.x + curr.width) - max(prev.x, curr.x); if (overlapX > 0 || (curr.x - (prev.x + prev.width)) <= 2) { currentGroup.push_back(curr); } else { int x_min = INT_MAX, y_min = INT_MAX, x_max = 0, y_max = 0; for (const Rect& r : currentGroup) { x_min = min(x_min, r.x); y_min = min(y_min, r.y); x_max = max(x_max, r.x + r.width); y_max = max(y_max, r.y + r.height); } merged.emplace_back(Point(x_min, y_min), Point(x_max, y_max)); currentGroup.clear(); currentGroup.push_back(curr); } } if (!currentGroup.empty()) { int x_min = INT_MAX, y_min = INT_MAX, x_max = 0, y_max = 0; for (const Rect& r : currentGroup) { x_min = min(x_min, r.x); y_min = min(y_min, r.y); x_max = max(x_max, r.x + r.width); y_max = max(y_max, r.y + r.height); } merged.emplace_back(Point(x_min, y_min), Point(x_max, y_max)); } return merged; } // ============================================================ // Step 4. 表达式求值 // ============================================================ double evalExpression(const string& expr) { auto precedence = [](char op) { if (op == '√')return 3;// if (op == '+' || op == '-') return 1; if (op == '*' || op == '/') return 2; return 0; }; auto apply = [](double a, double b, char op) { switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return (b != 0) ? a / b : 0.0; case'√':return sqrt(b);// default: return 0.0; } }; stack<double> values; stack<char> ops; string num; for (size_t i = 0; i < expr.size(); ++i) { char c = expr[i]; if (isdigit(c) || c == '.') { num += c; } else if (c == '√') { if (!num.empty()) { values.push(stod(num)); num.clear(); } while (!ops.empty() && precedence(ops.top()) > precedence(c)) { double b = values.top(); values.pop(); double a = values.top(); values.pop(); char op = ops.top(); ops.pop(); values.push(apply(a, b, op)); } ops.push(c); } else if (c == '(') { ops.push(c); } else if (c == ')') { if (!num.empty()) { values.push(stod(num)); num.clear(); } while (!ops.empty() && ops.top() != '(') { double b = values.top(); values.pop(); double a = values.top(); values.pop(); char op = ops.top(); ops.pop(); values.push(apply(a, b, op)); } ops.pop(); } else if (c == '+' || c == '-' || c == '*' || c == '/') { if (!num.empty()) { values.push(stod(num)); num.clear(); } while (!ops.empty() && ops.top()!='('&& precedence(ops.top()) >= precedence(c)) { double b = values.top(); values.pop(); double a = values.top(); values.pop(); char op = ops.top(); ops.pop(); values.push(apply(a, b, op)); } ops.push(c); } } if (!num.empty()) values.push(stod(num)); while (!ops.empty()) { double b = values.top(); values.pop(); double a = values.top(); values.pop(); char op = ops.top(); ops.pop(); values.push(apply(a, b, op)); } return values.empty() ? 0.0 : values.top(); } // ============================================================ // 主函数 // ============================================================ int main() { string input_path ="C:\\Users\\陈雪丽\\Pictures\\Screenshots\\屏幕截图 2025-11-11 233052.png" ; string output_dir = "D:\\output"; string template_dir = "D:\\template"; fs::create_directories(output_dir); Mat img = imread(input_path); if (img.empty()) { cerr << "❌ 未找到图像文件: " << input_path << endl; return -1; } Mat gray, blurImg, binary; cvtColor(img, gray, COLOR_BGR2GRAY); GaussianBlur(gray, blurImg, Size(3, 3), 0); adaptiveThreshold(blurImg, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 35, 15); // Step 2. 提取轮廓 vector<vector<Point>> contours; findContours(binary, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); vector<Rect> charBoxes; for (auto& c : contours) { Rect r = boundingRect(c); int area = r.width * r.height; if (area < 10) continue; if (area < 30 && !(r.width > 10 && r.height <= 4)) continue; charBoxes.push_back(r); } sort(charBoxes.begin(), charBoxes.end(), [](Rect a, Rect b) { return a.x < b.x; }); // Step 3. x方向重叠合并 charBoxes = mergeByXOverlap(charBoxes); cout << "检测到字符数量: " << charBoxes.size() << endl; // Step 4. 加载模板 auto templates = loadTemplates(template_dir); // Step 5. 模板名称映射 map<string, string> nameToSymbol = { {"0", "0"}, {"1", "1"}, {"2", "2"}, {"3", "3"}, {"4", "4"}, {"5", "5"}, {"6", "6"}, {"7", "7"}, {"8", "8"}, {"9", "9"}, {"add", "+"}, {"sub", "-"}, {"mul", "*"}, {"div", "/"}, {"eq", "="},{"left","("},{"right",")"},{"sqrt","√"}// }; // Step 6. 逐个匹配识别(阈值过滤) vector<string> recognized; int idx = 1; double matchThreshold = 0.6; // ✅ 匹配值阈值 for (auto& r : charBoxes) { Mat charImg = binary(r); double white_ratio = countNonZero(charImg) / double(charImg.total()); double black_ratio = 1.0 - white_ratio; string symbol; double score = 0.0; string reason; if (white_ratio > 0.9 || black_ratio > 0.9) { symbol = "-"; reason = "白/黑占比>90%,直接判为减号"; score = 1.0; } else { auto [name, val] = templateMatch(charImg, templates); score = val; if (score >= matchThreshold) { symbol = nameToSymbol.count(name) ? nameToSymbol[name] : name; reason = "模板匹配 score=" + to_string(score); } else { symbol = "未知"; reason = "score太低(" + to_string(score) + ")"; } } recognized.push_back(symbol); cout << "[" << idx << "] " << symbol << " | white=" << white_ratio << " black=" << black_ratio << " | " << reason << endl; Scalar color = (symbol == "未知") ? Scalar(0, 255, 255) : Scalar(0, 0, 255); rectangle(img, r, color, 1); putText(img, to_string(idx) + ":" + symbol, Point(r.x, r.y - 3), FONT_HERSHEY_SIMPLEX, 0.5, color, 1); idx++; } // Step 7. 拼接表达式并计算 string expression; for (auto& s : recognized) if (s != "=" && s != "未知") expression += s; cout << "表达式: " << expression << endl; double value = evalExpression(expression); cout << "计算结果: " << expression << " = " << value << endl; // Step 8. 显示结果 putText(img, "= " + to_string(value), Point(50, img.rows - 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2); imshow("Binary", binary); imshow("Calculator Result", img); waitKey(0); destroyAllWindows(); return 0; } 这是我的代码,在此基础上让它可以识别一张图片的多条式子并完成计算,不改动我其他的代码以及我的代码顺序
最新发布
11-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值