首页 > 其他分享 >食用串桶食嚼食鳖尝方杏

食用串桶食嚼食鳖尝方杏

时间:2024-11-09 22:30:39浏览次数:1  
标签:串桶 尝方杏 Point double lines3 255 second size 食嚼食

前言

我是傻逼

这是一个作业,不要看。

由于我太菜了做不到把 “文章” 显示在首页,所以只能发在随笔里。

不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。不要看。

阅读理解

#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

相关文章