前言
我是傻逼
这是一个作业,不要看。
由于我太菜了做不到把 “文章” 显示在首页,所以只能发在随笔里。
不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。
阅读理解
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
1.
int main(int argc, char *argv[]) 数量 数组
stod() 将字符串转化为 double
2.
createTrackbar("滑动条名字", "窗口名字", &变量, 最大值, emptytrackbar);
void emptytrackbar(int, void*){} empty 可共用
waitKey();
3.
VideoCapture vi("/dev/video0", CAP_V4L2); 取决于系统 摄像头类型 特定需求
vi.isOpened() bool 类型
vi.set(CAP_PROP_AUTO_EXPOSURE, 3.0); 自动曝光 1.0 关闭 3.0 打开 可以先打开再关闭
vi.set(CAP_PROP_EXPOSURE, 曝光强度);
vi.set(CAP_PROP_FRAME_WIDTH, 640); 或 HEIGHT 返回 bool 值是否成功
vi.set(CAP_PROP_FPS, 40); 帧率 返回 bool 值是否成功
vi.read(zhen) 提取 Mat 类型 zhen.empty()
vi.release();
destroyAllWindows();
4.
Rect roi = (左上角, 左上角, 右下角, 右下角);
Mat zhen = zhen0(roi)
GaussianBlur(输入, 输出, Size(核的宽度, 核的高度), 标准差); Size(5, 5), 0
cvtColor(输入, 输出, COLOR_BGR2HSV);
inRange(输入, Scalar(,,), Scalar(,,), mask); 中间会被设置为白色 其他为黑色
threshold(输入, 输出, 阈值, 最大值, THRESH_BINARY); >= 阈值的点会被设为最大值 < 的会被设为 0
5.
findContours(输入, 输出轮廓, 输出关系, RETR_TREE, CHAIN_APPROX_SIMPLE); 模式:检测嵌套 方法:保留端点
轮廓:vector 套 vector Point(Point: .x .y)
关系:Vector 套 Vec4i(即 vector <int, 4>)[0] 下一个 [1] 上一个 [2] 第一个儿子 [3] 父亲 没有为 -1
drawContours(图片, 轮廓们, 第几个轮廓, Scalar(,,), 粗细, LINE_8);
arcLength(轮廓, 是否封闭); 周长 double
approxPolyDP(轮廓, 输出, 精度, 是否封闭);
Douglas-Peucker 算法:连上首尾两个点,判断最远的点距离是否大于 epsilon,连线
polylines(图片, 轮廓, 是否封闭, Scalar(,,), 粗细); 画多边形
食鳖一哥标准的尝方杏
#include <iostream>
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <map>
using namespace std;
using namespace cv;
// 初始化HSV阈值的上下限
int lowH = 0, lowS = 0, lowV = 0;
int highH = 180, highS = 255, highV = 255;
#define HEIGHT 480
#define EPSILON_RATIO 0.05 //目前这个参数表现良好
#define MIN_AREA_RATIO 0.1 //目前这个参数表现良好
int ROI [] {150,150,300,180};
void emptytrackbar(int, void*){}
std::pair<std::vector<cv::Point>, std::vector<cv::Point>> findBox(cv::Mat& img_bin, cv::Mat& frame) {
if (img_bin.empty()) {
std::cout << "img_bin cannot be empty" << std::endl;
throw std::invalid_argument("img_bin cannot be empty");
}
int min_area = EPSILON_RATIO * img_bin.total();
std::map<int, std::vector<cv::Point>> ret;
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(img_bin, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
if (contours.empty()) {
return std::make_pair(std::vector<cv::Point>(), std::vector<cv::Point>());
}
for (size_t i = 0; i < contours.size(); i++) {
if (cv::contourArea(contours[i]) >= min_area) {
double epsilon = EPSILON_RATIO * cv::arcLength(contours[i], true);
std::vector<cv::Point> approx;
cv::approxPolyDP(contours[i], approx, epsilon, true);
//轮廓 输出 精度 是否封闭
if (approx.size() == 4) {
ret[i] = approx;
// for(int j = 0; j < approx.size(); ++j){
// printf("(%d, %d) ", approx[j].x, approx[j].y);
// }
// puts("");
}
}
}
for (size_t i = 0; i < contours.size(); i++) {
if (ret.count(i) && ret[i].size() == 4 && hierarchy[i][3] != -1 && ret.count(hierarchy[i][3]) && ret[hierarchy[i][3]].size() == 4 && hierarchy[i][2] == -1) {
// 是正方形 有父亲 父亲也是正方形 没儿子
if (!frame.empty()) {
cv::polylines(frame, ret[i], true, cv::Scalar(0, 255, 255), 10);
cv::polylines(frame, ret[hierarchy[i][3]], true, cv::Scalar(0, 255, 255), 10);
cv::drawContours(frame, contours, i, cv::Scalar(0, 0, 255), 2);
cv::drawContours(frame, contours, hierarchy[i][3], cv::Scalar(0, 0, 255), 2);
}
return std::make_pair(ret[hierarchy[i][3]], ret[i]);
}
}
return std::make_pair(std::vector<cv::Point>(), std::vector<cv::Point>());
}
int main(int argc, char *argv[]){
if(argc != 3){
puts("请输入 ./image_processor /dev/video0 230");
// 连接到电脑的第一个摄像头,设置曝光值为230
return -1;
}
// 一个接口类型
VideoCapture vi("/dev/video0", CAP_V4L2); // 取决于 系统 摄像头类型 特定需求
if(!vi.isOpened()){
puts("无法打开摄像头");
return -1;
}
double baoguangdu = stod(argv[2]);
vi.set(CAP_PROP_AUTO_EXPOSURE, 3.0); // 先打开自动曝光
// vi.set(CAP_PROP_AUTO_EXPOSURE, 1.0); // 再关闭自动曝光
// vi.set(CAP_PROP_EXPOSURE, baoguangdu); // 最后设置曝光参数
if(!vi.set(CAP_PROP_FRAME_WIDTH, 640) || !vi.set(CAP_PROP_FRAME_HEIGHT, 480)){
puts("无法设置图像大小"); // set 返回值都是 bool
return -1;
}
if(!vi.set(CAP_PROP_FPS, 40)){
puts("无法设置摄像头帧率");
return -1;
}
namedWindow("qwq", WINDOW_AUTOSIZE);
createTrackbar("Low H", "qwq", &lowH, 180, emptytrackbar);
createTrackbar("High H", "qwq", &highH, 180, emptytrackbar);
createTrackbar("Low S", "qwq", &lowS, 255, emptytrackbar);
createTrackbar("High S", "qwq", &highS, 255, emptytrackbar);
createTrackbar("Low V", "qwq", &lowV, 255, emptytrackbar);
createTrackbar("High V", "qwq", &highV, 255, emptytrackbar);
int MIN_AREA = int(MIN_AREA_RATIO * (ROI[2] - ROI[0]) * (ROI[3] - ROI[1])); // 0.1 * 150 * 180 降噪?
while(1){
Mat zhen0;
vi.read(zhen0); //和 vi >> zhen0 等价
if(zhen0.empty()){
puts("无法读取摄像头帧");
break;
}
Rect roi(ROI[0], ROI[1], ROI[2], ROI[3]); // 150 150 300 180 好像真要截了 ohhhhh
Mat zhen = zhen0(roi);
GaussianBlur(zhen, zhen, Size(5, 5), 0); // Size(核的宽度,核的高度), 标准差
Mat hsv;
cvtColor(zhen, hsv, COLOR_BGR2HSV);
Mat mask;
inRange(hsv, Scalar(lowH, lowS, lowV), Scalar(highH, highS, highV), mask); // 中间会被设置为白色
Mat binary;
zhen.copyTo(binary, mask); // low 和 high 之外的颜色会被设置为黑色
cvtColor(binary, binary, COLOR_BGR2GRAY);
// imshow("gray", binary);
threshold(binary, binary, 120, 255, THRESH_BINARY);
// imshow("binary", binary);
// imshow("zhen", zhen);
// 灰度图 结果 阈值 最大值 阈值类型( >= 阈值的点会变成最大值)
// 这阈值待定
auto boxex = findBox(binary, zhen);
vector <vector <Point> > lunkuos;
vector <Vec4i> guanxis; // 父轮廓 子轮廓 前轮廓 后轮廓
findContours(binary, lunkuos, guanxis, RETR_TREE, CHAIN_APPROX_SIMPLE); //模式:检测嵌套 方法:保留端点
Mat drawing = Mat::zeros(binary.size(), CV_8UC3);
for(int i = max(0, (int)(lunkuos.size() - 2)); i < lunkuos.size(); ++i)
drawContours(binary, lunkuos, i, Scalar(255, 255, 255), 2, LINE_8);
imshow("drawing", drawing);
imshow("binary", binary);
imshow("original", zhen);
int C = waitKey(30);
if(C == 27) break;
}
vi.release();
destroyAllWindows();
return 0;
}
垃圾
RotatedRect rect = minAreaRect(点集); 最小矩形覆盖
rect.size.width
rect.size.height
Point2f vertices[4]; Point 整数 Point2f 小数
rect.points(vertices); 把四个定点的坐标存进去
line(背景, 点, 点, Scalar(,,), 粗细);
to_string(数字)
putText(背景, "文字", 点, FONT_HERSHEY_SIMPLEX, 字体大小, Scalar(,,), 文本线宽);
鲨椰食鳖不料的呆码
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
// bool isrect(vector <int> lunkuo){
// vector <int> lunkuo2;
// }
int main(){
Mat img0 = imread("../img/board4.jpg");
if(img0.empty()){
cout << "Could not open or find the image" << endl;
return -1;
}
Mat img;
GaussianBlur(img0, img, Size(5, 5), 0); // 高斯模糊
img.convertTo(img, -1, 2, 0); // 对比度增强
cvtColor(img, img, COLOR_BGR2GRAY);
Mat erzhi;
threshold(img, erzhi, 250, 255, THRESH_BINARY);
int white = countNonZero(erzhi);
Mat element = getStructuringElement(cv::MORPH_RECT,Size(10, 10),cv::Point(-1, -1));
erode(erzhi, erzhi, element);
imshow("erzhi", erzhi);
vector <vector<Point>> contours;
vector <Vec4i> hierarchy;
findContours(erzhi, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
Mat bg = Mat::zeros(img.size(), CV_8UC3);
int maxarea = 0;
RotatedRect maxrect;
for(int i = 0; i < contours.size(); i++){
int A = contourArea(contours[i]);
int L = arcLength(contours[i], true);
if(A < 100) continue;
approxPolyDP(contours[i], contours[i], L * 0.05, 1);
RotatedRect rect = minAreaRect(contours[i]);
//比较 rect 的最大面积
int area = rect.size.width * rect.size.height;
if(area > maxarea){
maxarea = area;
maxrect = rect;
}
}
if(maxarea){
Point2f vertices[4];
maxrect.points(vertices);
for(int i = 0; i < 4; i++){
line(img0, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0), 10);
}
putText(img0, "Area: " + to_string(maxarea), Point(50, 50), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
}
imshow("qwq", img0);
waitKey(0);
return 0;
}
鸡蒜鸡核大法(图片版本 - 有问题)
思路:对白色部分进行霍夫变换,合并相近并相交的线(感觉有点蠢,这个是不是霍夫变换的时候调参就能出来了),鸡脚排序,扫描平行线和另一条线,取最大的平行四边形。
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <iostream>
using namespace std;
using namespace cv;
// bool isrect(vector <int> lunkuo){
// vector <int> lunkuo2;
// }
double vectorlen(double x, double y){
return sqrt(x * x + y * y);
}
double vectordianji(double x1, double y1, double x2, double y2){
return x1 * x2 + y1 * y2;
}
double vectorjiajiao(double x1, double y1, double x2, double y2){
double ang = acos(vectordianji(x1, y1, x2, y2) / (vectorlen(x1, y1) * vectorlen(x2, y2))) * 180 / CV_PI;
return min(ang, 180 - ang);
}
double linejiajiao(Vec4i l1, Vec4i l2){
double x1 = l1[2] - l1[0];
double y1 = l1[3] - l1[1];
double x2 = l2[2] - l2[0];
double y2 = l2[3] - l2[1];
return vectorjiajiao(x1, y1, x2, y2);
}
double linelen(Vec4i l){
return vectorlen(l[2] - l[0], l[3] - l[1]);
}
Mat img0;
Vec4i l3;
bool hebing(Vec4i l1, Vec4i l2){
// 在窗口 hebing 中显示 l1 和 l2
// Mat hebing = img0.clone();
// line(hebing, Point(l1[0], l1[1]), Point(l1[2], l1[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(hebing, Point(l2[0], l2[1]), Point(l2[2], l2[3]), Scalar(255, 0, 0), 2, LINE_AA);
// imshow("hebing", hebing);
// cout << "尝试合并:" << l1[0] << " " << l1[1] << " " << l1[2] << " " << l1[3] << " " << l2[0] << " " << l2[1] << " " << l2[2] << " " << l2[3] << endl;
// waitKey(0);
int x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3];
int x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];
l3 = Vec4i(x1, y1, x3, y3); // 这玩应 debug 了半天
Vec4i l4 = Vec4i(x1, y1, x4, y4);
Vec4i l5 = Vec4i(x2, y2, x3, y3);
Vec4i l6 = Vec4i(x2, y2, x4, y4);
double angle = 15; // 可以调整
if(linejiajiao(l1, l2) > angle) return 0;
// puts("角度合适");
// 找到 l3,l4,l5,l6 中最长的线
double len1 = linelen(l1), len2 = linelen(l2), len3 = linelen(l3), len4 = linelen(l4), len5 = linelen(l5), len6 = linelen(l6);
double maxlen = max(max(len3, len4), max(len5, len6));
if(maxlen == len3) l3 = Vec4i(x1, y1, x3, y3);
if(maxlen == len4) l3 = Vec4i(x1, y1, x4, y4);
if(maxlen == len5) l3 = Vec4i(x2, y2, x3, y3);
if(maxlen == len6) l3 = Vec4i(x2, y2, x4, y4);
// cout << "最长线为:" << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
if(linejiajiao(l1, l3) > angle || linejiajiao(l2, l3) > angle) return 0;
// puts("最长线角度合适");
if(len1 > maxlen) maxlen = len1, l3 = l1;
if(len2 > maxlen) maxlen = len2, l3 = l2;
if(len1 + len2 < maxlen) return 0;
// puts("长度合适");
// 在窗口 hebing 中显示 l3
// line(hebing, Point(l3[0], l3[1]), Point(l3[2], l3[3]), Scalar(0, 0, 255), 2, LINE_AA);
// imshow("hebing", hebing);
// puts("合并成功");
// cout << "合并后:" << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
// waitKey(0);
return 1;
}
int main(int argc, char** argv){
if(argc != 2){
cout << "Usage: " << argv[0] << " <Image_Path>" << endl;
return -1;
}
img0 = imread(argv[1]);
if(img0.empty()){
cout << "Could not open or find the image" << endl;
return -1;
}
//缩小图片高度为 hei
int hei = 600;
resize(img0, img0, Size(img0.cols * hei / img0.rows, hei));
Mat img;
GaussianBlur(img0, img, Size(5, 5), 0); // 高斯模糊
img.convertTo(img, -1, 1.8, 0); // 对比度增强
// imshow("img", img);
cvtColor(img, img, COLOR_BGR2GRAY);
Mat erzhi;
threshold(img, erzhi, 250, 255, THRESH_BINARY);
// int white = countNonZero(erzhi);
// Mat element = getStructuringElement(cv::MORPH_RECT,Size(10, 10),cv::Point(-1, -1));
// erode(erzhi, erzhi, element);
// imshow("erzhi", erzhi);
vector <Vec4i> lines;
HoughLinesP(erzhi, lines, 1, CV_PI/180, 50, 50, 10);
Mat img2 = img0.clone();
for(size_t i = 0; i < lines.size(); ++i){
Vec4i l = lines[i];
line(img2, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img2", img2);
//将两条特别近的线合并
vector <Vec4i> lines2;
for(size_t i = 0; i < lines.size(); ++i){
// cout << "滴滴滴,再说一遍 l3:" << l3[0] << ' ' << l3[1] << ' ' << l3[2] << ' ' << l3[3] << endl;
Vec4i l = lines[i];
if(linelen(l) < 10) continue; // 可以调整
// cout << "第 " << i << " 轮\n";
bool flag = 0;
for(size_t j = 0; j < lines2.size(); ++j){
Vec4i l2 = lines2[j];
if(hebing(l, l2)){
flag = 1;
lines2[j] = l3;
// cout << "lines2[" << j << "] = " << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
// puts("change");
}
}
if(!flag) lines2.push_back(l);
// if(!flag) puts("add new");
// Mat img3 = img0.clone();
// cout << "lines2.size() = " << lines2.size() << endl;
// for(size_t j = 0; j < lines2.size(); ++j){
// Vec4i l = lines2[j];
// cout << "lines2[" << j << "] = " << l[0] << " " << l[1] << " " << l[2] << " " << l[3] << endl;
// line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
// }
// imshow("img3", img3);
// puts("已显示所有 lines2");
// waitKey(0);
}
Mat img3 = img0.clone();
for(size_t i = 0; i < lines2.size(); ++i){
Vec4i l = lines2[i];
line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img3", img3);
// 对 lines2 中的线进行极角排序
vector <pair <double, Vec4i> > lines3;
for(size_t i = 0; i < lines2.size(); ++i){
Vec4i l = lines2[i];
double angle = atan2(l[3] - l[1], l[2] - l[0]);
lines3.push_back(make_pair(angle, l));
}
sort(lines3.begin(), lines3.end(), [](pair <double, Vec4i> a, pair <double, Vec4i> b){
return a.first < b.first;
});
Vec4i ansi, ansj, ansk;
double ansxik, ansxjk, ansyik, ansyjk;
double maxarea = 0;
for(size_t i = 0; i < lines3.size(); ++i){
for(size_t j = i + 1; j < lines3.size(); ++j){
double angle1 = 15, angle2 = 30;
if(linejiajiao(lines3[i].second, lines3[j].second) > angle1) continue;
for(size_t k = 0; k < lines3.size(); ++k){
// 画出 i,j,k
// Mat img4 = img0.clone();
// line(img4, Point(lines3[i].second[0], lines3[i].second[1]), Point(lines3[i].second[2], lines3[i].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(img4, Point(lines3[j].second[0], lines3[j].second[1]), Point(lines3[j].second[2], lines3[j].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(img4, Point(lines3[k].second[0], lines3[k].second[1]), Point(lines3[k].second[2], lines3[k].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// imshow("img4", img4);
// waitKey(0);
if(linejiajiao(lines3[i].second, lines3[k].second) < angle2) continue;
if(linejiajiao(lines3[j].second, lines3[k].second) < angle2) continue;
// 求出 i和k,j和k 的交点
int xik, yik, xjk, yjk;
// 考虑除以 0 的情况
if((lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]) == 0){
xik = lines3[i].second[0], yik = lines3[k].second[1];
xjk = lines3[j].second[0], yjk = lines3[k].second[1];
}
if((lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]) == 0){
xik = lines3[i].second[0], yik = lines3[k].second[1];
xjk = lines3[j].second[0], yjk = lines3[k].second[1];
}
else{
xik = (lines3[i].second[0] * lines3[i].second[3] - lines3[i].second[2] * lines3[i].second[1]) * (lines3[k].second[0] - lines3[k].second[2]) - (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[0] * lines3[k].second[3] - lines3[k].second[2] * lines3[k].second[1]);
yik = (lines3[i].second[1] * lines3[i].second[2] - lines3[i].second[3] * lines3[i].second[0]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[1] * lines3[k].second[2] - lines3[k].second[3] * lines3[k].second[0]);
xik /= (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
yik /= (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
xjk = (lines3[j].second[0] * lines3[j].second[3] - lines3[j].second[2] * lines3[j].second[1]) * (lines3[k].second[0] - lines3[k].second[2]) - (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[0] * lines3[k].second[3] - lines3[k].second[2] * lines3[k].second[1]);
yjk = (lines3[j].second[1] * lines3[j].second[2] - lines3[j].second[3] * lines3[j].second[0]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[1] * lines3[k].second[2] - lines3[k].second[3] * lines3[k].second[0]);
xjk /= (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
yjk /= (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
}
xik = abs(xik), yik = abs(yik), xjk = abs(xjk), yjk = abs(yjk);
// cout << "xik = " << xik << " yik = " << yik << " xjk = " << xjk << " yjk = " << yjk << endl;
// 在 img4 画出交点
// circle(img4, Point(xik, yik), 5, Scalar(0, 0, 255), -1);
// circle(img4, Point(xjk, yjk), 5, Scalar(0, 0, 255), -1);
// imshow("img4", img4);
// waitKey(0);
// 判断交点和线段端点的曼哈顿距离是否小于 dis
int dis = 30;
if(abs(xik - lines3[k].second[0]) + abs(yik - lines3[k].second[1]) > dis && abs(xik - lines3[k].second[2]) + abs(yik - lines3[k].second[3]) > dis) continue;
if(abs(xjk - lines3[k].second[0]) + abs(yjk - lines3[k].second[1]) > dis && abs(xjk - lines3[k].second[2]) + abs(yjk - lines3[k].second[3]) > dis) continue;
if(abs(xik - lines3[i].second[0]) + abs(yik - lines3[i].second[1]) > dis && abs(xik - lines3[i].second[2]) + abs(yik - lines3[i].second[3]) > dis) continue;
if(abs(xjk - lines3[j].second[0]) + abs(yjk - lines3[j].second[1]) > dis && abs(xjk - lines3[j].second[2]) + abs(yjk - lines3[j].second[3]) > dis) continue;
// 计算 i 和 j 围成的四边形的面积
double area = abs((lines3[i].second[0] - lines3[j].second[0]) * (lines3[i].second[3] - lines3[j].second[3]) - (lines3[i].second[1] - lines3[j].second[1]) * (lines3[i].second[2] - lines3[j].second[2])) / 2;
// cout << "area = " << area << endl;
if(area > maxarea){
maxarea = area;
ansi = lines3[i].second;
ansj = lines3[j].second;
ansk = lines3[k].second;
ansxik = xik, ansyik = yik, ansxjk = xjk, ansyjk = yjk;
}
}
}
}
Mat outimg = img0.clone();
if(maxarea){
if(linelen(ansi) < linelen(ansj)) swap(ansi, ansj), swap(ansxik, ansxjk), swap(ansyik, ansyjk);
int xx, yy;
if(abs(ansi[0] - ansxik) + abs(ansi[1] - ansyik) > abs(ansi[2] - ansxik) + abs(ansi[3] - ansyik)) xx = ansi[0], yy = ansi[1];
else xx = ansi[2], yy = ansi[3];
int xxx = ansxjk + xx - ansxik, yyy = ansyjk + yy - ansyik;
Scalar color = Scalar(0, 255, 0);
line(outimg, Point(ansxik, ansyik), Point(xx, yy), color, 2, LINE_AA);
line(outimg, Point(xx, yy), Point(xxx, yyy), color, 2, LINE_AA);
line(outimg, Point(xxx, yyy), Point(ansxjk, ansyjk), color, 2, LINE_AA);
line(outimg, Point(ansxjk, ansyjk), Point(ansxik, ansyik), color, 2, LINE_AA);
double area = abs((xx - ansxjk) * (yy - ansyjk) - (xx - ansxjk) * (yy - ansyjk)) / 2;
putText(outimg, "Area: " + to_string(maxarea), Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("outimg", outimg);
// waitKey(0);
return 0;
}
问题:比较面积的时候应该用平行四边形面积
修复版(视频版本)
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <iostream>
using namespace std;
using namespace cv;
// bool isrect(vector <int> lunkuo){
// vector <int> lunkuo2;
// }
double vectorlen(double x, double y){
return sqrt(x * x + y * y);
}
double vectordianji(double x1, double y1, double x2, double y2){
return x1 * x2 + y1 * y2;
}
double vectorjiajiao(double x1, double y1, double x2, double y2){
double ang = acos(vectordianji(x1, y1, x2, y2) / (vectorlen(x1, y1) * vectorlen(x2, y2))) * 180 / CV_PI;
return min(ang, 180 - ang);
}
double linejiajiao(Vec4i l1, Vec4i l2){
double x1 = l1[2] - l1[0];
double y1 = l1[3] - l1[1];
double x2 = l2[2] - l2[0];
double y2 = l2[3] - l2[1];
return vectorjiajiao(x1, y1, x2, y2);
}
double linelen(Vec4i l){
return vectorlen(l[2] - l[0], l[3] - l[1]);
}
Mat img0;
Vec4i l3;
bool hebing(Vec4i l1, Vec4i l2){
// 在窗口 hebing 中显示 l1 和 l2
// Mat hebing = img0.clone();
// line(hebing, Point(l1[0], l1[1]), Point(l1[2], l1[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(hebing, Point(l2[0], l2[1]), Point(l2[2], l2[3]), Scalar(255, 0, 0), 2, LINE_AA);
// imshow("hebing", hebing);
// cout << "尝试合并:" << l1[0] << " " << l1[1] << " " << l1[2] << " " << l1[3] << " " << l2[0] << " " << l2[1] << " " << l2[2] << " " << l2[3] << endl;
// waitKey(0);
int x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3];
int x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];
l3 = Vec4i(x1, y1, x3, y3); // 这玩应 debug 了半天
Vec4i l4 = Vec4i(x1, y1, x4, y4);
Vec4i l5 = Vec4i(x2, y2, x3, y3);
Vec4i l6 = Vec4i(x2, y2, x4, y4);
double angle = 15; // 可以调整
if(linejiajiao(l1, l2) > angle) return 0;
// puts("角度合适");
// 找到 l3,l4,l5,l6 中最长的线
double len1 = linelen(l1), len2 = linelen(l2), len3 = linelen(l3), len4 = linelen(l4), len5 = linelen(l5), len6 = linelen(l6);
double maxlen = max(max(len3, len4), max(len5, len6));
if(maxlen == len3) l3 = Vec4i(x1, y1, x3, y3);
if(maxlen == len4) l3 = Vec4i(x1, y1, x4, y4);
if(maxlen == len5) l3 = Vec4i(x2, y2, x3, y3);
if(maxlen == len6) l3 = Vec4i(x2, y2, x4, y4);
// cout << "最长线为:" << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
if(linejiajiao(l1, l3) > angle || linejiajiao(l2, l3) > angle) return 0;
// puts("最长线角度合适");
if(len1 > maxlen) maxlen = len1, l3 = l1;
if(len2 > maxlen) maxlen = len2, l3 = l2;
if(len1 + len2 + 30 < maxlen) return 0;
// puts("长度合适");
// 在窗口 hebing 中显示 l3
// line(hebing, Point(l3[0], l3[1]), Point(l3[2], l3[3]), Scalar(0, 0, 255), 2, LINE_AA);
// imshow("hebing", hebing);
// puts("合并成功");
// cout << "合并后:" << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
// waitKey(0);
return 1;
}
double getarea(Vec4i ansi, Vec4i ansj, Vec4i ansk, double ansxik, double ansyik, double ansxjk, double ansyjk){
// Mat outimg = Mat::zeros(Size(600, 600), CV_8UC3);
if(linelen(ansi) < linelen(ansj)) swap(ansi, ansj), swap(ansxik, ansxjk), swap(ansyik, ansyjk);
int xx, yy;
if(abs(ansi[0] - ansxik) + abs(ansi[1] - ansyik) > abs(ansi[2] - ansxik) + abs(ansi[3] - ansyik)) xx = ansi[0], yy = ansi[1];
else xx = ansi[2], yy = ansi[3];
int xxx = ansxjk + xx - ansxik, yyy = ansyjk + yy - ansyik;
// Scalar color = Scalar(0, 255, 0);
// line(outimg, Point(ansxik, ansyik), Point(xx, yy), color, 2, LINE_AA);
// line(outimg, Point(xx, yy), Point(xxx, yyy), color, 2, LINE_AA);
// line(outimg, Point(xxx, yyy), Point(ansxjk, ansyjk), color, 2, LINE_AA);
// line(outimg, Point(ansxjk, ansyjk), Point(ansxik, ansyik), color, 2, LINE_AA);
double area = vectorlen(abs(xx - ansxjk), abs(yy - ansyjk)) * vectorlen(abs(xxx - ansxik), abs(yyy - ansyik)) / 2;
// cout << "Area: " << area << endl;
// putText(outimg, "Area: " + to_string(area), Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2, LINE_AA);
// imshow("outimg", outimg);
// waitKey(0);
return area;
}
Mat chuli(Mat img0){
//缩小图片高度为 hei
int hei = 600;
resize(img0, img0, Size(img0.cols * hei / img0.rows, hei));
Mat img;
GaussianBlur(img0, img, Size(5, 5), 0); // 高斯模糊
// 亮度减弱
// img.convertTo(img, -1, 0, -1);S
img.convertTo(img, -1, 2.5, -100); // 对比度增强
// imshow("img", img);
// return img;
cvtColor(img, img, COLOR_BGR2GRAY);
Mat erzhi;
threshold(img, erzhi, 250, 255, THRESH_BINARY);
// int white = countNonZero(erzhi);
// Mat element = getStructuringElement(cv::MORPH_RECT,Size(10, 10),cv::Point(-1, -1));
// erode(erzhi, erzhi, element);
// imshow("erzhi", erzhi);
// return erzhi;
// 边缘检测
Canny(erzhi, erzhi, 50, 150, 3);
// imshow("canny", erzhi);
// waitKey(0);
// return erzhi;
vector <Vec4i> lines;
HoughLinesP(erzhi, lines, 1, CV_PI/180, 50, 20, 30);
// 参数分别为:输入图像,输出直线,距离精度,角度精度,阈值,最小线段长度,最大线段间隔
Mat img2 = img0.clone();
for(size_t i = 0; i < lines.size(); ++i){
Vec4i l = lines[i];
line(img2, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img2", img2);
// return img2;
//将两条特别近的线合并
vector <Vec4i> lines2;
for(size_t i = 0; i < lines.size(); ++i){
// cout << "滴滴滴,再说一遍 l3:" << l3[0] << ' ' << l3[1] << ' ' << l3[2] << ' ' << l3[3] << endl;
Vec4i l = lines[i];
if(linelen(l) < 10) continue; // 可以调整
// cout << "第 " << i << " 轮\n";
bool flag = 0;
for(size_t j = 0; j < lines2.size(); ++j){
Vec4i l2 = lines2[j];
if(hebing(l, l2)){
flag = 1;
lines2[j] = l3;
// cout << "lines2[" << j << "] = " << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
// puts("change");
}
}
if(!flag) lines2.push_back(l);
// if(!flag) puts("add new");
// Mat img3 = img0.clone();
// cout << "lines2.size() = " << lines2.size() << endl;
// for(size_t j = 0; j < lines2.size(); ++j){
// Vec4i l = lines2[j];
// cout << "lines2[" << j << "] = " << l[0] << " " << l[1] << " " << l[2] << " " << l[3] << endl;
// line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
// }
// imshow("img3", img3);
// puts("已显示所有 lines2");
// waitKey(0);
}
Mat img3 = img0.clone();
for(size_t i = 0; i < lines2.size(); ++i){
Vec4i l = lines2[i];
line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img3", img3);
return img3;
// 对 lines2 中的线进行极角排序
vector <pair <double, Vec4i> > lines3;
for(size_t i = 0; i < lines2.size(); ++i){
Vec4i l = lines2[i];
double angle = atan2(l[3] - l[1], l[2] - l[0]);
lines3.push_back(make_pair(angle, l));
}
sort(lines3.begin(), lines3.end(), [](pair <double, Vec4i> a, pair <double, Vec4i> b){
return a.first < b.first;
});
Vec4i ansi, ansj, ansk;
double ansxik, ansxjk, ansyik, ansyjk;
double maxarea = 0;
for(size_t i = 0; i < lines3.size(); ++i){
for(size_t j = i + 1; j < lines3.size(); ++j){
double angle1 = 15, angle2 = 30;
if(linejiajiao(lines3[i].second, lines3[j].second) > angle1) continue;
for(size_t k = 0; k < lines3.size(); ++k){
// 画出 i,j,k
// Mat img4 = img0.clone();
// line(img4, Point(lines3[i].second[0], lines3[i].second[1]), Point(lines3[i].second[2], lines3[i].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(img4, Point(lines3[j].second[0], lines3[j].second[1]), Point(lines3[j].second[2], lines3[j].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// line(img4, Point(lines3[k].second[0], lines3[k].second[1]), Point(lines3[k].second[2], lines3[k].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
// imshow("img4", img4);
// waitKey(0);
if(linejiajiao(lines3[i].second, lines3[k].second) < angle2) continue;
if(linejiajiao(lines3[j].second, lines3[k].second) < angle2) continue;
// 求出 i和k,j和k 的交点
int xik, yik, xjk, yjk;
// 考虑除以 0 的情况
if((lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]) == 0){
xik = lines3[i].second[0], yik = lines3[k].second[1];
xjk = lines3[j].second[0], yjk = lines3[k].second[1];
}
if((lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]) == 0){
xik = lines3[i].second[0], yik = lines3[k].second[1];
xjk = lines3[j].second[0], yjk = lines3[k].second[1];
}
else{
xik = (lines3[i].second[0] * lines3[i].second[3] - lines3[i].second[2] * lines3[i].second[1]) * (lines3[k].second[0] - lines3[k].second[2]) - (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[0] * lines3[k].second[3] - lines3[k].second[2] * lines3[k].second[1]);
yik = (lines3[i].second[1] * lines3[i].second[2] - lines3[i].second[3] * lines3[i].second[0]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[1] * lines3[k].second[2] - lines3[k].second[3] * lines3[k].second[0]);
xik /= (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
yik /= (lines3[i].second[0] - lines3[i].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[i].second[1] - lines3[i].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
xjk = (lines3[j].second[0] * lines3[j].second[3] - lines3[j].second[2] * lines3[j].second[1]) * (lines3[k].second[0] - lines3[k].second[2]) - (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[0] * lines3[k].second[3] - lines3[k].second[2] * lines3[k].second[1]);
yjk = (lines3[j].second[1] * lines3[j].second[2] - lines3[j].second[3] * lines3[j].second[0]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[1] * lines3[k].second[2] - lines3[k].second[3] * lines3[k].second[0]);
xjk /= (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
yjk /= (lines3[j].second[0] - lines3[j].second[2]) * (lines3[k].second[1] - lines3[k].second[3]) - (lines3[j].second[1] - lines3[j].second[3]) * (lines3[k].second[0] - lines3[k].second[2]);
}
xik = abs(xik), yik = abs(yik), xjk = abs(xjk), yjk = abs(yjk);
// cout << "xik = " << xik << " yik = " << yik << " xjk = " << xjk << " yjk = " << yjk << endl;
// 在 img4 画出交点
// circle(img4, Point(xik, yik), 5, Scalar(0, 0, 255), -1);
// circle(img4, Point(xjk, yjk), 5, Scalar(0, 0, 255), -1);
// imshow("img4", img4);
// waitKey(0);
// 判断交点和线段端点的曼哈顿距离是否小于 dis
int dis = 30;
if(abs(xik - lines3[k].second[0]) + abs(yik - lines3[k].second[1]) > dis && abs(xik - lines3[k].second[2]) + abs(yik - lines3[k].second[3]) > dis) continue;
if(abs(xjk - lines3[k].second[0]) + abs(yjk - lines3[k].second[1]) > dis && abs(xjk - lines3[k].second[2]) + abs(yjk - lines3[k].second[3]) > dis) continue;
if(abs(xik - lines3[i].second[0]) + abs(yik - lines3[i].second[1]) > dis && abs(xik - lines3[i].second[2]) + abs(yik - lines3[i].second[3]) > dis) continue;
if(abs(xjk - lines3[j].second[0]) + abs(yjk - lines3[j].second[1]) > dis && abs(xjk - lines3[j].second[2]) + abs(yjk - lines3[j].second[3]) > dis) continue;
// 计算 i 和 j 围成的四边形的面积
double area = getarea(lines3[i].second, lines3[j].second, lines3[k].second, xik, yik, xjk, yjk);
// double area = abs((lines3[i].second[0] - lines3[j].second[0]) * (lines3[i].second[3] - lines3[j].second[3]) - (lines3[i].second[1] - lines3[j].second[1]) * (lines3[i].second[2] - lines3[j].second[2])) / 2;
// cout << "area = " << area << endl;
if(area > maxarea){
maxarea = area;
ansi = lines3[i].second;
ansj = lines3[j].second;
ansk = lines3[k].second;
ansxik = xik, ansyik = yik, ansxjk = xjk, ansyjk = yjk;
}
}
}
}
Mat outimg = img0.clone();
if(maxarea){
if(linelen(ansi) < linelen(ansj)) swap(ansi, ansj), swap(ansxik, ansxjk), swap(ansyik, ansyjk);
int xx, yy;
if(abs(ansi[0] - ansxik) + abs(ansi[1] - ansyik) > abs(ansi[2] - ansxik) + abs(ansi[3] - ansyik)) xx = ansi[0], yy = ansi[1];
else xx = ansi[2], yy = ansi[3];
int xxx = ansxjk + xx - ansxik, yyy = ansyjk + yy - ansyik;
Scalar color = Scalar(0, 255, 0);
line(outimg, Point(ansxik, ansyik), Point(xx, yy), color, 2, LINE_AA);
line(outimg, Point(xx, yy), Point(xxx, yyy), color, 2, LINE_AA);
line(outimg, Point(xxx, yyy), Point(ansxjk, ansyjk), color, 2, LINE_AA);
line(outimg, Point(ansxjk, ansyjk), Point(ansxik, ansyik), color, 2, LINE_AA);
double area = vectorlen(abs(xx - ansxjk), abs(yy - ansyjk)) * vectorlen(abs(xxx - ansxik), abs(yyy - ansyik)) / 2;
putText(outimg, "Area: " + to_string(area), Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("outimg", outimg);
// waitKey(0);
return outimg;
}
int main(){
VideoCapture shuru("../img/koulan3.mp4");
Mat zhen;
int frame_width = shuru.get(CAP_PROP_FRAME_WIDTH);
int frame_height = shuru.get(CAP_PROP_FRAME_HEIGHT);
int frame_nums = shuru.get(CAP_PROP_FRAME_COUNT);
double frame_fps = shuru.get(CAP_PROP_FPS);
// VideoWriter shuchu("../img/newkoulan1.mp4",shuru.get(CAP_PROP_FOURCC),frame_fps,Size(frame_width,frame_height),true);
while (1) {
shuru.read(zhen);
if(zhen.empty()) break;
zhen = chuli(zhen);
// shuchu.write(zhen);
imshow("video", zhen);
int c = waitKey(10);
if (c == 27) break;
}
shuru.release();
// shuchu.release();
return 0;
}
上面那个是直接识别大白像素块的,这个是识别边缘的。
缺点是边缘到了下面会弯识别不上,感觉必须考虑四条边。
先把球框放大再说吧。
最后一坨
目前还没有测试视频,但是不想写了。/fn/fn/fn
下次不在主函数里写计算几何了。/fn
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
using namespace cv;
vector <Mat> tuxiangmen;
void emptytrackbar(int, void *){}
int lowH = 150, lowS = 60, lowV = 220;
int highH = 180, highS = 120, highV = 255;
double vectorlen(double x, double y){
return sqrt(x * x + y * y);
}
double vectordianji(double x1, double y1, double x2, double y2){
return x1 * x2 + y1 * y2;
}
double vectorjiajiao(double x1, double y1, double x2, double y2){
double ang = acos(vectordianji(x1, y1, x2, y2) / (vectorlen(x1, y1) * vectorlen(x2, y2))) * 180 / CV_PI;
return min(ang, 180 - ang);
}
double linejiajiao(Vec4i l1, Vec4i l2){
double x1 = l1[2] - l1[0];
double y1 = l1[3] - l1[1];
double x2 = l2[2] - l2[0];
double y2 = l2[3] - l2[1];
return vectorjiajiao(x1, y1, x2, y2);
}
double linelen(Vec4i l){
return vectorlen(l[2] - l[0], l[3] - l[1]);
}
Point jiaodian(Vec4i l1, Vec4i l2){
// cout << l1[0] << ' ' << l1[1] << ' ' << l1[2] << ' ' << l1[3] << endl;
// cout << l2[0] << ' ' << l2[1] << ' ' << l2[2] << ' ' << l2[3] << endl;
if(l1[0] == l1[2] || l2[0] == l2[2]){
if(l2[0] == l2[2]) swap(l1, l2);
double k2 = (double)(l2[3] - l2[1]) / (l2[2] - l2[0]);
double yy = (double)l2[1] - k2 * (l2[0] - l1[0]);
// cout << l1[0] << ' ' << yy << endl;
return Point(l1[0], yy);
}else{
double k1 = (double)(l1[3] - l1[1]) / (l1[2] - l1[0]);
double b1 = l1[1] - k1 * l1[0];
double k2 = (double)(l2[3] - l2[1]) / (l2[2] - l2[0]);
double b2 = l2[1] - k2 * l2[0];
double xx = (b2 - b1) / (k1 - k2);
double yy = k1 * xx + b1;
// cout << xx << ' ' << yy << endl;
return Point(xx, yy);
}
}
bool pointinmid(Point po, Point p1, Point p2){
int LEN = vectorlen(p1.x - p2.x, p1.y - p2.y);
if(vectorlen(p1.x - po.x, p1.y - po.y) <= LEN && vectorlen(p2.x - po.x, p2.y - po.y) <= LEN) return 1;
return 0;
}
bool lineinmid(Vec4i l, Point p1, Point p2){
if(!pointinmid(Point(l[0], l[1]), p1, p2)) return 0;
if(!pointinmid(Point(l[2], l[3]), p1, p2)) return 0;
return 1;
}
double getarea(Point p1, Point p2, Point p3, Point p4){
double l1 = vectorlen(p2.x - p1.x, p2.y - p1.y);
double l2 = vectorlen(p4.x - p3.x, p4.y - p3.y);
return l1 * l2 / 2;
}
Mat img0;
Vec4i l3;
bool hebing(Vec4i l1, Vec4i l2){
int x1 = l1[0], y1 = l1[1], x2 = l1[2], y2 = l1[3];
int x3 = l2[0], y3 = l2[1], x4 = l2[2], y4 = l2[3];
l3 = Vec4i(x1, y1, x3, y3); // 这玩应 debug 了半天
Vec4i l4 = Vec4i(x1, y1, x4, y4);
Vec4i l5 = Vec4i(x2, y2, x3, y3);
Vec4i l6 = Vec4i(x2, y2, x4, y4);
double angle = 3; // 可以调整
if(linejiajiao(l1, l2) > angle) return 0;
// puts("角度合适");
// 找到 l3,l4,l5,l6 中最长的线
double len1 = linelen(l1), len2 = linelen(l2), len3 = linelen(l3), len4 = linelen(l4), len5 = linelen(l5), len6 = linelen(l6);
double maxlen = max(max(len3, len4), max(len5, len6));
if(maxlen == len3) l3 = Vec4i(x1, y1, x3, y3);
if(maxlen == len4) l3 = Vec4i(x1, y1, x4, y4);
if(maxlen == len5) l3 = Vec4i(x2, y2, x3, y3);
if(maxlen == len6) l3 = Vec4i(x2, y2, x4, y4);
// cout << "最长线为:" << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
if(linejiajiao(l1, l3) > angle || linejiajiao(l2, l3) > angle) return 0;
// puts("最长线角度合适");
if(len1 > maxlen) maxlen = len1, l3 = l1;
if(len2 > maxlen) maxlen = len2, l3 = l2;
if(len1 + len2 + 30 < maxlen) return 0;
return 1;
}
bool FLAG;
Mat getrect(Mat img0){
Mat img;
img0.convertTo(img, -1, 2.5, -100); // 对比度增强
// imshow("img", img);
// return img;
cvtColor(img, img, COLOR_BGR2GRAY);
Mat erzhi;
threshold(img, erzhi, 250, 255, THRESH_BINARY);
// int white = countNonZero(erzhi);
// Mat element = getStructuringElement(cv::MORPH_RECT,Size(10, 10),cv::Point(-1, -1));
// erode(erzhi, erzhi, element);
// imshow("erzhi", erzhi);
// return erzhi;
// 边缘检测
Canny(erzhi, erzhi, 50, 150, 3);
// imshow("canny", erzhi);
// waitKey(0);
// return erzhi;
vector <Vec4i> lines;
HoughLinesP(erzhi, lines, 1, CV_PI/180, 50, 30, 20);
// 参数分别为:输入图像,输出直线,距离精度,角度精度,阈值,最小线段长度,最大线段间隔
Mat img2 = img0.clone();
for(size_t i = 0; i < lines.size(); ++i){
Vec4i l = lines[i];
line(img2, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img2", img2);
// return img2;
//将两条特别近的线合并
vector <Vec4i> lines2;
for(size_t i = 0; i < lines.size(); ++i){
// cout << "滴滴滴,再说一遍 l3:" << l3[0] << ' ' << l3[1] << ' ' << l3[2] << ' ' << l3[3] << endl;
Vec4i l = lines[i];
if(linelen(l) < 10) continue; // 可以调整
// cout << "第 " << i << " 轮\n";
bool flag = 0;
for(size_t j = 0; j < lines2.size(); ++j){
Vec4i l2 = lines2[j];
if(hebing(l, l2)){
flag = 1;
lines2[j] = l3;
// cout << "lines2[" << j << "] = " << l3[0] << " " << l3[1] << " " << l3[2] << " " << l3[3] << endl;
// puts("change");
}
}
if(!flag) lines2.push_back(l);
// if(!flag) puts("add new");
// Mat img3 = img0.clone();
// cout << "lines2.size() = " << lines2.size() << endl;
// for(size_t j = 0; j < lines2.size(); ++j){
// Vec4i l = lines2[j];
// cout << "lines2[" << j << "] = " << l[0] << " " << l[1] << " " << l[2] << " " << l[3] << endl;
// line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
// }
// imshow("img3", img3);
// puts("已显示所有 lines2");
// waitKey(0);
}
Mat img3 = img0.clone();
for(size_t i = 0; i < lines2.size(); ++i){
Vec4i l = lines2[i];
line(img3, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 255, 0), 2, LINE_AA);
}
// imshow("img3", img3);
// return img3;
// 对 lines2 中的线进行极角排序
vector <pair <double, Vec4i> > lines3;
for(size_t i = 0; i < lines2.size(); ++i){
//长度小于 10 的线段不要
if(linelen(lines2[i]) < 200) continue;
Vec4i l = lines2[i];
double angle = atan2(l[3] - l[1], l[2] - l[0]);
lines3.push_back(make_pair(angle, l));
}
sort(lines3.begin(), lines3.end(), [](pair <double, Vec4i> a, pair <double, Vec4i> b){
return a.first < b.first;
});
double maxarea = 0;
Point ans[4];
for(size_t i = 0; i < lines3.size(); ++i){
for(size_t j = i + 1; j < lines3.size(); ++j){
double angle1 = 5, angle2 = 30;
if(linejiajiao(lines3[i].second, lines3[j].second) > angle1) continue;
for(size_t k = i + 1; k < lines3.size(); ++k){
for(size_t l = k + 1; l < lines3.size(); ++l){
// cout << i << ' ' << j << ' ' << k << ' ' << l << endl;
if(linejiajiao(lines3[k].second, lines3[l].second) > angle1) continue;
if(linejiajiao(lines3[i].second, lines3[k].second) < angle2) continue;
Mat img4 = img0.clone();
putText(img4, to_string(linejiajiao(lines3[i].second, lines3[k].second)), Point(lines3[i].second[0], lines3[i].second[1]), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
//画出来四条线
line(img4, Point(lines3[i].second[0], lines3[i].second[1]), Point(lines3[i].second[2], lines3[i].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
line(img4, Point(lines3[j].second[0], lines3[j].second[1]), Point(lines3[j].second[2], lines3[j].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
line(img4, Point(lines3[k].second[0], lines3[k].second[1]), Point(lines3[k].second[2], lines3[k].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
line(img4, Point(lines3[l].second[0], lines3[l].second[1]), Point(lines3[l].second[2], lines3[l].second[3]), Scalar(0, 255, 0), 2, LINE_AA);
//求出 i 和 k 的交点
Point p1 = jiaodian(lines3[k].second, lines3[i].second);
Point p2 = jiaodian(lines3[i].second, lines3[l].second);
Point p3 = jiaodian(lines3[l].second, lines3[j].second);
Point p4 = jiaodian(lines3[j].second, lines3[k].second);
//画出四个交点
circle(img4, p1, 5, Scalar(0, 0, 255), -1);
circle(img4, p2, 5, Scalar(0, 0, 255), -1);
circle(img4, p3, 5, Scalar(0, 0, 255), -1);
circle(img4, p4, 5, Scalar(0, 0, 255), -1);
if(!lineinmid(lines3[i].second, p1, p2)) continue;
if(!lineinmid(lines3[l].second, p2, p3)) continue;
if(!lineinmid(lines3[j].second, p3, p4)) continue;
if(!lineinmid(lines3[k].second, p4, p1)) continue;
// imshow("img4,", img4);
// waitKey(0);
// 判断 i,j,k,l 的长度之和是否小于 (HEI + WID) * 2
// if(linelen(lines3[i].second) + linelen(lines3[j].second) + linelen(lines3[k].second) + linelen(lines3[l].second) < (HEI + WID)) continue;
// 求出 jk, ik, il, jl 四个点围成的四边形的面积
double area = getarea(p1, p2, p3, p4);
if(area > maxarea){
maxarea = area;
ans[0] = p1, ans[1] = p2, ans[2] = p3, ans[3] = p4;
}
}
}
}
}
Mat outimg = img0.clone();
// imshow("img0", img0);
if(maxarea){
line(outimg, ans[0], ans[1], Scalar(0, 255, 0), 2, LINE_AA);
line(outimg, ans[1], ans[2], Scalar(0, 255, 0), 2, LINE_AA);
line(outimg, ans[2], ans[3], Scalar(0, 255, 0), 2, LINE_AA);
line(outimg, ans[3], ans[0], Scalar(0, 255, 0), 2, LINE_AA);
putText(outimg, "Area: " + to_string(maxarea), Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 255, 0), 2);
}
imshow("outimg", outimg);
waitKey(0);
return outimg;
}
Mat chuli(Mat img0){
GaussianBlur(img0, img0, Size(5, 5), 0); // 高斯模糊
//查找橘色的区域
// return img0;
Mat hsv;
cvtColor(img0, hsv, COLOR_BGR2HSV);
Mat mask1, mask2, mask;
// inRange(hsv, Scalar(156, 100, 100), Scalar(180, 255, 255), mask1);
inRange(hsv, Scalar(lowH, lowS, lowV), Scalar(highH, highS, highV), mask);
// Mat mask = mask1 | mask2;
//转化为二值图像
Mat erzhi0, erzhi;
threshold(mask, erzhi0, 250, 255, THRESH_BINARY);
// tuxiangmen.push_back(erzhi0);
// erode(erzhi0, erzhi, getStructuringElement(MORPH_RECT, Size(5, 5)));
dilate(erzhi0, erzhi, getStructuringElement(MORPH_RECT, Size(15, 15)));
// bitwise_not(erzhi0, erzhi);
// erode(erzhi, erzhi, getStructuringElement(MORPH_RECT, Size(10, 10)));
// tuxiangmen.push_back(erzhi);
//识别轮廓
vector <vector <Point> > contours;
vector <Vec4i> hierarchy;
findContours(erzhi, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
//遍历每个轮廓
for(size_t i = 0; i < contours.size(); ++i){
double area = contourArea(contours[i]);
if(area < 100) continue;
//找到轮廓的最小外接矩形
Rect rect = boundingRect(contours[i]);
int w = rect.width, h = rect.height;
if(w * h < 1000) continue;
rect.x -= w * 1.2, rect.y -= w * 2, rect.width += w * 2.4, rect.height += w * 2.4;
//把超出边界的地方裁掉
if(rect.x < 0) rect.width += rect.x, rect.x = 0;
if(rect.y < 0) rect.height += rect.y, rect.y = 0;
if(rect.x + rect.width > img0.cols) rect.width = img0.cols - rect.x;
if(rect.y + rect.height > img0.rows) rect.height = img0.rows - rect.y;
//画出轮廓的最小外接矩形
// rectangle(img0, rect, Scalar(0, 255, 0), 2);
FLAG = 0;
Mat img1 = img0(rect);
int hh = 600;
resize(img1, img1, Size(img1.cols * hh / img1.rows, hh));
img1 = getrect(img1);
resize(img1, img1, rect.size());
img1.copyTo(img0(rect));
// waitKey(0);
}
return img0;
}
int main(int argc, char** argv){
if(argc != 2){
cout << "Usage: " << argv[0] << " <Image_Path>" << endl;
return -1;
}
img0 = imread(argv[1]);
if(img0.empty()){
cout << "Could not open or find the image" << endl;
return -1;
}
img0 = getrect(img0);
imshow("img0", img0);
waitKey(0);
return 0;
}
// int main(){
// Mat zhen;
// namedWindow("video", WINDOW_AUTOSIZE);
// // cv::createTrackbar("Low H", "video", &lowH, 180, emptytrackbar);
// // cv::createTrackbar("High H", "video", &highH, 180, emptytrackbar);
// // cv::createTrackbar("Low S", "video", &lowS, 255, emptytrackbar);
// // cv::createTrackbar("High S", "video", &highS, 255, emptytrackbar);
// // cv::createTrackbar("Low V", "video", &lowV, 255, emptytrackbar);
// // cv::createTrackbar("High V", "video", &highV, 255, emptytrackbar);
// // VideoWriter shuchu("../img/newkoulan1.mp4",shuru.get(CAP_PROP_FOURCC),frame_fps,Size(frame_width,frame_height),true);
// //让视频循环播放
// while(1){
// VideoCapture shuru("../img/koulan3.mp4");
// while (1) {
// tuxiangmen.clear();
// shuru.read(zhen);
// if(zhen.empty()) break;
// int hei = 300;
// resize(zhen, zhen, Size(zhen.cols * hei / zhen.rows, hei));
// // tuxiangmen.push_back(zhen);
// zhen = chuli(zhen);
// // shuchu.write(zhen);
// tuxiangmen.push_back(zhen);
// Mat result;
// hconcat(tuxiangmen, result);
// imshow("video", result);
// int c = waitKey(10);
// if (c == 27) return 0;
// }
// shuru.release();
// }
// // shuchu.release();
// return 0;
// }
标签:串桶,尝方杏,Point,double,lines3,255,second,size,食嚼食
From: https://www.cnblogs.com/fideow/p/18503065