狼牙战队10-07任务

算法组考核任务报告

  • 学院:计算机科学与技术学院
  • 专业班级:计算机科学与技术 2001 班
  • 姓名:刘景宇
  • 意向组:算法组

自我感觉应该不会像C语言/数据结构实验报告那么正式吧,所以就写我认为重要的内容了

所有代码的源码将会放在最后的附录中,函数声明以及部分重要代码会在任务的报告中出现。

目录

  • 任务一
  • 任务二
  • 任务三
  • 任务四
  • 任务五
  • 任务六
  • 任务七
  • 附录

任务一

  1. C++创建一个名为complex的结构体,结构体成员分别为:

    • float型复数的模
    • 长度为2的float型数组,存储复数的常数、复数项
    struct complex{
        float len;          //float类型数据,存放模长
        float num[2];       //float类型数组,存放实部和虚部
    };
    
  2. 编写函数,分别实现复数的加法、乘法、共轭操作,并将结果打印到屏幕上。

    • 打印的实现(加、乘、共轭操作都要打印)

      按照a+bi的形式打印

      如果虚部为非负数,则打印一个加号,否则不打印符号(因为负数自带减号)

    • 加法的实现

      定义一个变量c存放结果,作为返回值

      对应项相加,并计算相加后的复数的模

      打印结果

    complex add(const complex& c1,const complex& c2);
    
    • 乘法的实现

      定义一个变量c作为存放计算结果,作为返回值

      按照复数相乘的规则计算,对应的实部和虚部

      打印结果

    complex mut(const complex& c1,const complex& c2);
    
    • 共轭操作的实现

      定义一个变量c存放结果,作为返回值

      实部直接赋值,虚部赋相反数,模直接赋值

      打印结果

    complex con(const complex& c1);
    

    形参使用“常引用”要比直接复制一份效果好一点点。

  3. 定义一个复数类,创建构造函数,调用构造函数时完成对两个复数的复制操作。

    • private:存放数据,不允许外部读取更改
    • public:相关的函数
    • Complex(float x,float y)构造函数:接收两个浮点型数据,作为复数的实部和虚部(目前不考虑重载,这里认为用户输入的均为标准复数),有实部和虚部后立即计算模并存储下来。(这里只保留了基本的构造函数,省略了复制构造函数)
    • ~Complex()析构函数,写析构函数(尽管函数体为空),一个好的习惯
    class Complex{
        private:
        float len;
        float num[2];
        public:
        Complex(float x,float y);
      	~Complex(){}
        void add(const Complex& c);//求和
        void mut(const Complex& c);//乘积
        void con();//共轭操作
        void print();
    };
    Complex::Complex(float x,float y){
      //类外定义构造函数
        num[0]=x;
        num[1]=y;
        len=sqrt(x*x+y*y);//计算模长
        print();
    }
    
  4. 用类成员函数完成2中对复数的操作

    • 打印

      功能同结构体部分的print()函数

    void Complex::print();
    
    • 相加

      调用对象的add()函数时,传入一个复数对象,将结果保存在调用对象的数据中,并更改模长数据

    void Complex::add(const Complex& c);
    
    • 相乘

      调用对象的mul()函数的,传入一个复数对象,将结果保存在调用对象的数据中,并更改模长

    void Complex::mut(const Complex& c);
    
    • 共轭操作

      调用对象的con()函数的,该操作只需要更改虚部的政府即可,实部、模长的数据不需要改

    void Complex::con();
    
  5. 运行结果

    • 第一部分为结构体complex的测试结果
    • 第二部分为类Complex的测试结果
      在这里插入图片描述

任务二

  1. 随机生成长度为10的浮点型的一位数组

    • 随机生成一个浮点数的函数(在main函数中设置了srand(time(0)以保证每次得到的10个随机数完全不同)
    double randnum(){//随机生成一个浮点数
        return rand()/double(RAND_MAX)*1000;
      //为了使数据明显,将0-1的浮点数扩大1000倍
    }
    
    • 生成10个随机数

      for循环执行10次,每次得到一个随机数,用数组存下来

  2. 使用直接插入排序的方法对数组进行排序

    • 为了实现直接插入排序,创建数组时,创建num[11],把num[0]预留出来作为哨兵,可以防止越界(避免了越界检查)

    • 从第二个数开始:先把位置i上的数放到num[0]中,再依次和前一个数比较,如果小于前一个数,那么就将前一个数填入到后一个数的位置,直到碰到第一个比它大的数,那么就将num[0]填入这个位置(重复以上操作)

    关键代码:

    for(int i=2;i<=10;i++){
            x[0]=x[i];//设置哨兵,很好地避免了越界
            int j=i;
            while(x[0] < x[j-1]){
                x[j]=x[j-1];//把前一个数据放到这里
                j--;
            }
            x[j]=x[0];
    }
    
  3. 给出每一次插入排序后的排序效果,并给出最终的排序结果(如下图所示):
    在这里插入图片描述

  4. 给出排序的最终结果以及算法复杂度

  • 空间复杂度:O(n)

    只需要存放数的一维数组的大小。

  • 时间复杂度:O(n2)

    该程序的时间复杂度主要由排序函数造成,在排序过程中,外部从2-n循环,内部从i到1循环,其余部分均为线性,据此简单来看时间复杂度是O(n2)。

任务三

  1. 用C++构建一个类,类成员函数包括矩阵相乘(m*n矩阵,用二维数组表示)、矩阵相加、矩阵转置以及矩阵求逆。

    • 矩阵的类的定义

      m行n列

      为了方便调用,使用二维数组来表示矩阵,数字的类型定为double,开辟100*100的二维数组,已经可以表示常见的大多数矩阵

    ​ 因为在定义时,设置了double num[100][100],所以要避免传参数的时候复制一份传过去,这里的形参采用了常引用的类型,既避免了拷贝花费太多的时间,同时能够保证数据的安全性

    class Matrix{
        public:
        int m;//m行n列的矩阵
        int n;
        double num[100][100];//为了方便地使用二维数组,未使用vector高级
        Matrix();//无参构造函数,构造一个空矩阵
        Matrix(int m0,int n0);//两个参数的构造函数,构造一个有行有列的矩阵
        ~Matrix(){}
        void add(const Matrix& m1,const Matrix& m2);//相加函数
        void mut(const Matrix& m1,const Matrix& m2);//相乘函数
        void tran(const Matrix& m1);//转置函数
        void rebel(const Matrix& m1);//求m1的逆矩阵
        void print();//打印当前矩阵
    };
    
    • 构造函数

      在构造函数中设置矩阵的行列,以及矩阵中的数据,并且按行输入矩阵中的数据

      由于形参设置成了常引用,而非值传递,在这里没有编写复制构造函数

    Matrix::Matrix();
    Matrix::Matrix(int m0,int n0):m(m0),n(n0);
    
    • 析构函数

      为了形成良好的习惯,写析构函数,提醒在有new的时候要delete

    • 矩阵相加

      检查行列数是否对应相等,如果不等直接结束,并给出错误信息,行列数相同时,对应项直接相加,行列数直接复制一份。

    void Matrix::add(const Matrix& m1,const Matrix& m2){
        if(m2.m!=m1.m||m2.n!=m1.n){
            cout<<"不合法操作,矩阵维度不匹配"<<endl;
            return;
        }
        m=m1.m;n=m1.n;
        for(int i=1;i<=m1.m;i++)
            for(int j=1;j<=m1.n;j++)
                num[i][j]=m1.num[i][j]+m2.num[i][j];//对应项相加
        cout<<"矩阵求和结果:"<<endl;
        print();
    }
    
    • 矩阵相乘

      检查是否满足前一个矩阵的列数是否等于后一个矩阵的行数,如果不等直接结束,并给出相关的错误信息,如果满足前文中的条件,那么就按照矩阵的乘法进行计算,并更新行列数

    void Matrix::mut(const Matrix& m1,const Matrix& m2){
        //实现矩阵的乘法
        if(m1.n!=m2.m){
            cout<<"不合法操作,矩阵的维度不匹配"<<endl;
            return;
        }
        m=m1.m;
        n=m2.n;
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                double tmp=0;
                for(int k=1;k<=m1.n;k++){
                    tmp+=m1.num[i][k]*m2.num[k][j];
                }
                num[i][j]=tmp;
            }
        }
        cout<<"矩阵求乘积结果:"<<endl;
        print();   
    }
    
    • 矩阵转置

      只要将行列数交换,并且按照对应的关系进行赋值即可。

    void Matrix::tran(const Matrix& m1){
        //完成矩阵的转置
        m=m1.n;
        n=m1.m;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                num[i][j]=m1.num[j][i];
        cout<<"矩阵转置结果:"<<endl;
        print();
    }
    
    • 求逆矩阵

      这里采用伴随矩阵的方式求逆矩阵

      只有方阵(行列数相等)时,才可以求逆矩阵,在求逆矩阵前,先判断行列数是否相等,如果行列数不相等,那么可以直接返回,并提示错误信息

      如果相等,那么就可以进行下一步

      如果矩阵的对应的行列式的值为0,那么不可逆直接结束(矩阵不可逆条件),否则先求出来伴随矩阵,根据伴随矩阵求逆矩阵(注意伴随矩阵求的时候,要转置)

    求行列式的值时,采用递归的思想求解,结束条件是对应的矩阵的秩为1

    在求伴随矩阵时,调用求行列式的值的函数

    
    //计算行列式A的值,只有方阵才谈论逆矩阵
    double getA(const double x[][100],int n){
        //递归求解A的行列式的值
        if(n==1){//递归结束条件,只有一个值
            return x[1][1];
        }
        double num=0;//用来记录行列式的值
        double tmp[100][100];//用来copy一份余子式对应的矩阵
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
                tmp[i][j]=0;
        }
        for(int i=1;i<=n;i++){//第i个
            for(int j=1;j<n;j++){
                for(int k=1;k<n;k++){
                    tmp[j][k]=x[j+1][k>=i?k+1:k];//复制一份,但是要避开i所在的那一列
                }
            }
            double mid=getA(tmp,n-1);
            if(i%2){
                num+=x[1][i]*mid;//奇数时,为正
            }
            else{
                num-=x[1][i]*mid;//偶数时,为负
            }
        }
        return num;//返回计算结果
    }
    void adjMat(const double x[][100],int n,double res[][100]){
        //求伴随矩阵,求出伴随矩阵,那么逆矩阵就基本完成了
        if(n==1){
            res[1][1]=x[1][1];
            return;
        }
        double tmp[100][100];
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++)
            tmp[i][j]=0;
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){//外层循环用来检查
                for(int k=1;k<n;k++){//内层循环用来copy出来代数余子式那部分对应的矩阵
                    for(int l=1;l<n;l++){
                        tmp[k][l]=x[k>=i?k+1:k][l>=j?l+1:l];
                    }
                }
                //计算代数余子树
                res[j][i]=getA(tmp,n-1);//转置
                if((i+j)%2&&res[j][i]!=0){
                   res[j][i]=-res[j][i];
                }
            }
        }
    }
    void Matrix::rebel(const Matrix& m1){
        //求矩阵的逆
        if(m1.m!=m1.n){
            cout<<"该矩阵不是方阵"<<endl;
            return;
        }
        double A=getA(m1.num,m1.n);
        if(!A){
            cout<<"该矩阵不可逆"<<endl;
            return ;
        }
        adjMat(m1.num,m1.m,num);//结果存放在了新建的矩阵中
         m=n=m1.n;
         for(int i=1;i<=m;i++){
             for(int j=1;j<=n;j++)
             num[i][j]/=A;
         }
        cout<<"矩阵的逆为:"<<endl;
        print();
    }
    
  2. 调用类的函数进行测试,打印出结果。
    在这里插入图片描述

任务四

  1. 提取其中的装甲板(蓝色框的内部的平行四边形)

  2. 画出找到的四边形并保存为“result.jpg”

    一、思路一

    该思路来自于文档矫正部分,找出面积最大的部分,并且符合该任务的目标-找到装甲板

    • 利用findCountours函数识别所有轮廓
    vector<vector<Point> >contours;
    vector<Vec4i>hierarchy;//寻找轮廓-成功存放在contours中
    findContours(srcImg,contours,hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
    
    • 由于装甲板的面积最大,尝试使用比较面积从而找到

      max为装甲板(面积最大)轮廓的索引,contours[max]即代表装甲板的轮廓

    int max=0;
    double maxArea=0;
    for(int i=0;i<contours.size();++i){
        double area=contourArea(contours[i]);
        if(area>maxArea){
            max=i;
            maxArea=area;
        }
    }
    
    • 画出装甲板轮廓
    //画出装甲板轮廓
    drawContours(l,contours,max,Scalar(255,0,0),2);
    
    • 画出包围装甲板的最小矩形
    //画出最小矩形
    RotatedRect ROI=minAreaRect(contours[max]);
    Point2f P[4];
    ROI.points(P);
    for(int j=0;j<4;j++){
        line(l,P[j],P[(j+1)%4],Scalar(0,0,255),2,8);
    }
    
    • 结果如下:

      在这里插入图片描述

    • 保存结果

    imwrite("./result.png",l);
    

    ​ 二、思路二

    只是在此作为考虑,由于时间原因,没有进行实现

    任务中提示可以考虑漫水填充算法,在此进行考虑

    • 利用findContours函数找到所有轮廓

    利用漫水填充算法要求我们必须给出一个种子点,然后从此点进行漫水填充,但是这个点有没有一个比较好的方式得到

    考虑以下方式:

    1. 把图片放到PS中,量取装甲板上的一个点,并将此点作为种子点
    2. 找到包围面积最大的那个轮廓(即思路一中的方式),取这个轮廓上的一个点作为种子点
    • 取一个种子点
    • 利用漫水填充 floodFill函数进行填充

任务五

  1. 进行一些基本的变换(平移,旋转,缩放),结果以png格式保存

    • 平移
    srcTriangle[0]=Point2f(0,0);
    srcTriangle[1]=Point2f(0,1);
    srcTriangle[2]=Point2f(1,0);
    //平移-设置向右平行50个单位;
    dstTriangle[0]=Point2f(50,0);
    dstTriangle[1]=Point2f(50,1);
    dstTriangle[2]=Point2f(51,0);
    warpMat=getAffineTransform(srcTriangle,dstTriangle);//得到变换矩阵
    
    • 旋转
    Point center=Point(dstImg_warp.cols/2,dstImg_warp.rows/2);
    double angle=-30.0;
    rotMat=getRotationMatrix2D(center,angle,1);//得到旋转的变化矩阵
    
    • 缩放
    double scale=0.7;
    rotMat=getRotationMatrix2D(center,0,scale);//得到缩放的变换矩阵
    
    • 利用warpAffine函数进行求解整个图像变换后的图像
    • 原图、平移、旋转、缩放依次如下图所示
      在这里插入图片描述

在这里插入图片描述

  1. 识别一张倾斜拍摄的纸张,找出轮廓,提取纸张的位置

  2. 通过图像处理的算法找到了发生形变的纸张的位置,那么对这个倾斜的纸张进行变换,得到纸张垂直视图,实现文档校准

    一、思路一

    任务四的思路一救来源于这里

    当然这里的思路借鉴了网上的想法

    目前没有一种很好的方式确定轮廓,但是呢,由于手机拍摄出来的纸张都还是图像里面积最大的(因此,这里完全够用)

    • findContours函数找出所有轮廓

    • 遍历contours,利用contourArea函数找到面积最大的轮廓,该轮廓大概率就是我们要找的轮廓(依据常识以及认为习惯来看–该结论还是很准的)

    • 找到轮廓的四周的四个点,这里有三种方式

    1. 直接打开PS测量,但是受限制比较多
    2. 利用凸包进行计算:在利用convexHull函数寻找凸包前,先对所有轮廓利用approxPolyDP函数进行多边形逼近处理,这样能够比较好的得到一个边界比较直的轮廓,其实就是为了提高凸包计算时的准确性
    • 确定输出图像的大小,利用getPerspectiveTransform函数求解透视变换的矩阵
     Mat wrapMatrix=getPerspectiveTransform(srcpoint,dstpoint);
    
    • 利用warpPerspective函数进行透视变换
    warpPerspective(srcImg,dstImg_pers,wrapMatrix,dstImg_pers.size());
    
    • 变换结果
      在这里插入图片描述

    二、思路二

    霍夫变换直线检测,利用不平行的两直线相交于一点,求解文档的轮廓以及四个顶点,然后进行透视变换

    霍夫变换的优势:能够克服纸张缺损的情况,比较强大

    劣势:考虑的情况比较多,过程比较复杂

    (还未进行验证)

任务六

  1. 对灰度图进行直方图均衡化,并将处理后的图片保存

    直接调用equalizeHist

    (当然前提是灰度图–这里读取的原图就是一张灰度图)

    equalizeHist(srcImg,dst);
    
    • 结果(上面是原图、下面是直方图均衡化后的图)

在这里插入图片描述
在这里插入图片描述

  1. 对恢复图的像素点进行统计,将统计的结果打印到屏幕上
    • 调用calcHist函数统计直方图的信息
    • 定义了getHistImage函数,在该函数内部实现了直方图的绘制

在这里插入图片描述

  1. 对RGB图像进行颜色空间转换,转换到HSV空间

    cvtColor(s,s,COLOR_BGR2HSV);
    

    在这里插入图片描述
    在这里插入图片描述

任务七

之前了解过一点python,但并没有总结过,近期,在廖雪峰的官方网站学习python,并记录了一些没见过的知识点

python的基本语法基本掌握,但尚缺乏联系,不够熟练。

(笔记还未来得及进行格式整理,只是根据学习的一些内容,先快速过了一遍,为了减少占用的篇幅,在此将-python随笔-上传CSDN,随后再进行整理,由以下链接可进入)

https://blog.csdn.net/jingyu_1/article/details/120633348

(该博客也是我从上半年来使用至今的博客)

可以用python完成面向过程的编程,可以完成对类的编写,完成基础的面向对象编程。

看了一些网络资源,了解了一些相机标定的内容。

正常尝试安装Ubuntu系统,由于电脑存储空间不够,没有办法在当前电脑上装新系统或者装虚拟机,所以网购了一个三星的移动固态硬盘,打算尝试将Ubuntu系统安装到移动固态硬盘上,目前遇到一些困难,正在尝试。

附录

#include <iostream>
#include <math.h>
using namespace std;
//结构体相关函数
struct complex{
    float len;          //存放模长
    float num[2];       //存放实部和虚部
};
void print(const complex& c){
    cout<<c.num[0];
    if(c.num[1]>=0)
        cout<<"+";
    cout<<c.num[1]<<"i";
    cout<<"\t"<<"len="<<c.len<<endl;
}
complex add(const complex& c1,const complex& c2){
    complex c;
    c.num[0]=c1.num[0]+c2.num[0];
    c.num[1]=c1.num[1]+c2.num[1];
    c.len=sqrt(c.num[0]*c.num[0]+c.num[1]*c.num[1]);//模相乘
    print(c);
    return c;
}
complex mut(const complex& c1,const complex& c2){
    complex c;
    c.num[0]=c1.num[0]*c2.num[0]-c1.num[1]*c2.num[1];//实部
    c.num[1]=c1.num[0]*c2.num[1]+c1.num[1]*c2.num[0];//虚部
    c.len=c1.len*c2.len;
    print(c);
    return c;
}
complex con(const complex& c1){
    complex c;
    c.num[0]=c1.num[0];
    c.num[1]=-c1.num[1];
    c.len=c1.len;
    print(c);
    return c;
}
//类相关函数
class Complex{
    private:
    float len;
    float num[2];
    public:
    Complex(float x,float y);
    ~Complex(){}
    void add(const Complex& c);
    void mut(const Complex& c);
    void con();
    void print();
};

Complex::Complex(float x,float y){
    num[0]=x;
    num[1]=y;
    len=sqrt(x*x+y*y);
    print();
}
void Complex::add(const Complex& c){
    num[0]=num[0]+c.num[0];
    num[1]=num[1]+c.num[1];
    len=sqrt(num[0]*num[0]+num[1]*num[1]);
    print();
}
void Complex::mut(const Complex& c){
    num[0]=num[0]*c.num[0]-num[1]*c.num[1];
    len=len*c.len;
    print();
}
void Complex::con(){
    num[1]=-num[1];
    print();
}
void Complex::print(){
    cout<<num[0];
    if(num[1]>=0)
        cout<<"+";
    cout<<num[1]<<"i";
    cout<<"\t"<<"len="<<len<<endl;
}
int main(){//测试
    complex c,c1,c2;
    cout<<"\n请输入 两个复数的 实部和虚部:\n";
    cin>>c1.num[0]>>c1.num[1];
    c1.len=sqrt(c1.num[0]*c1.num[0]+c1.num[1]*c1.num[1]);
    cin>>c2.num[0]>>c2.num[1];
    c2.len=sqrt(c2.num[0]*c2.num[0]+c2.num[1]*c2.num[1]);
    cout<<"c1=";print(c1);
    cout<<"c2=";print(c2);
    cout<<"c1+c2=";c=add(c1,c2);
    cout<<"c1*c2=";c=mut(c1,c2);
    cout<<"c1的共轭:";c=con(c1);
    cout<<"\n请输入 两个复数的 实部和虚部:\n";
    float x1,x2,y1,y2;
    cin>>x1>>y1>>x2>>y2;
    cout<<"c1=";
    Complex com1(x1,y1);
    cout<<"c2=";
    Complex com2(x2,y2);
    cout<<"c1+c2=";
    com1.add(com2);
    cout<<"(c1+c2)*c2=";
    com1.add(com2);
    cout<<"c1*c2的共轭:";
    com1.con();
    return 0;
}
#include <iostream>
using namespace std;
double num[11];//单独空出来一个用作哨兵
double randnum(){
    //随机生成一个浮点数
    return rand()/double(RAND_MAX)*1000;//为了使数据明显,将0-1的浮点数扩大1000倍
}
void print(double x[],int n){
    for(int i=1;i<n;i++){
        cout<<x[i]<<" ";
    }
    cout<<endl;
}
void sort(double x[],int n){
    //对数组进行排序
    for(int i=2;i<=10;i++){
        x[0]=x[i];//设置哨兵,很好地避免了越界
        int j=i;
        while(x[0] < x[j-1]){
            x[j]=x[j-1];//把前一个数据放到这里
            j--;
        }
        x[j]=x[0];
        print(x,n);//按要求,每次都要输出结果
    }
}
int main(){
    srand(time(0));
    for(int i=1;i<=10;++i){//随机生成10个浮点数
        num[i]=randnum();
    }
    cout<<"初始数据:";
    print(num,11);
    cout<<endl;
    sort(num,11);//对10个数进行排序,11中包括一个无值的哨兵
    cout<<endl<<"排序结果:";
    print(num,11);
    return 0;
}
/**
 * @Author: Your name
 * @Date:   2021-09-22 20:34:26
 * @Last Modified by:   Your name
 * @Last Modified time: 2021-10-07 10:14:10
 */
#include <iostream>
using namespace std;
class Matrix{
    public:
    int m;//m行n列的矩阵
    int n;
    double num[100][100];//为了方便地使用二维数组,未使用vector高级
    Matrix();//构造一个空矩阵
    Matrix(int m0,int n0);//构造一个有行有列的矩阵
    ~Matrix(){}
    void add(const Matrix& m1,const Matrix& m2);
    void mut(const Matrix& m1,const Matrix& m2);
    void tran(const Matrix& m1);
    void rebel(const Matrix& m1);
    void print();
};
void Matrix::print(){
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            cout<<num[i][j]<<" ";
        }
        cout<<endl;
    }
    cout<<endl;
}
Matrix::Matrix(){
    m=n=0;
    cout<<"已构造一个空的矩阵,可以保存计算结果"<<endl<<endl;
}
Matrix::Matrix(int m0,int n0):m(m0),n(n0){
    cout<<"请输入矩阵各行各列的值"<<endl;
    for(int i=1;i<=m0;++i)
        for(int j=1;j<=n0;++j)
            cin>>num[i][j];
    cout<<"输入的矩阵的为:"<<endl;
    print();
}
void Matrix::add(const Matrix& m1,const Matrix& m2){
    if(m2.m!=m1.m||m2.n!=m1.n){
        cout<<"不合法操作,矩阵维度不匹配"<<endl;
        return;
    }
    m=m1.m;n=m1.n;
    for(int i=1;i<=m1.m;i++)
        for(int j=1;j<=m1.n;j++)
            num[i][j]=m1.num[i][j]+m2.num[i][j];//对应项相加
    cout<<"矩阵求和结果:"<<endl;
    print();
}
void Matrix::mut(const Matrix& m1,const Matrix& m2){
    //实现矩阵的乘法
    if(m1.n!=m2.m){
        cout<<"不合法操作,矩阵的维度不匹配"<<endl;
        return;
    }
    m=m1.m;
    n=m2.n;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            double tmp=0;
            for(int k=1;k<=m1.n;k++){
                tmp+=m1.num[i][k]*m2.num[k][j];
            }
            num[i][j]=tmp;
        }
    }
    cout<<"矩阵求乘积结果:"<<endl;
    print();
}
void Matrix::tran(const Matrix& m1){
    //完成矩阵的转置
    m=m1.n;
    n=m1.m;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            num[i][j]=m1.num[j][i];
    cout<<"矩阵转置结果:"<<endl;
    print();
}
//计算行列式A的值,只有方阵才谈论逆矩阵
double getA(const double x[][100],int n){
    //递归求解A的行列式的值
    if(n==1){//递归结束条件,只有一个值
        return x[1][1];
    }
    double num=0;//用来记录行列式的值
    double tmp[100][100];//用来copy一份余子式对应的矩阵
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            tmp[i][j]=0;
    }
    for(int i=1;i<=n;i++){//第i个
        for(int j=1;j<n;j++){
            for(int k=1;k<n;k++){
                tmp[j][k]=x[j+1][k>=i?k+1:k];//复制一份,但是要避开i所在的那一列
            }
        }
        double mid=getA(tmp,n-1);
        if(i%2){
            num+=x[1][i]*mid;//奇数时,为正
        }
        else{
            num-=x[1][i]*mid;//偶数时,为负
        }
    }
    return num;//返回计算结果
}
void adjMat(const double x[][100],int n,double res[][100]){
    //求伴随矩阵,求出伴随矩阵,那么逆矩阵就基本完成了
    if(n==1){
        res[1][1]=x[1][1];
        return;
    }
    double tmp[100][100];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
        tmp[i][j]=0;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){//外层循环用来检查
            for(int k=1;k<n;k++){//内层循环用来copy出来代数余子式那部分对应的矩阵
                for(int l=1;l<n;l++){
                    tmp[k][l]=x[k>=i?k+1:k][l>=j?l+1:l];
                }
            }
            //计算代数余子树
            res[j][i]=getA(tmp,n-1);//转置
            if((i+j)%2&&res[j][i]!=0){
               res[j][i]=-res[j][i];
            }
        }
    }
}
void Matrix::rebel(const Matrix& m1){
    //求矩阵的逆
    if(m1.m!=m1.n){
        cout<<"该矩阵不是方阵"<<endl;
        return;
    }
    double A=getA(m1.num,m1.n);
    if(!A){
        cout<<"该矩阵不可逆"<<endl;
        return ;
    }
    adjMat(m1.num,m1.m,num);//结果存放在了新建的矩阵中
     m=n=m1.n;
     for(int i=1;i<=m;i++){
         for(int j=1;j<=n;j++)
         num[i][j]/=A;
     }
    cout<<"矩阵的逆为:"<<endl;
    print();
}
int main()
{
    //检验以上类和函数是否正确可行
    Matrix m1(4,4);
    Matrix m2(4,4);
    Matrix m;
    m.add(m1,m2);
    m.mut(m1,m2);
    m.tran(m1);
    cout<<"矩阵的行列式"<<getA(m1.num,m1.n)<<endl;
    m.rebel(m1);
    return 0;
}
/**
 * @Author: Your name
 * @Date:   2021-09-29 20:11:36
 * @Last Modified by:   Your name
 * @Last Modified time: 2021-10-07 17:17:12
 */
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main(int argc, char const *argv[])
{
    Mat srcImg;
    //没有加0,出现的问题
    srcImg=imread("/Users/liujingyu/Desktop/2022赛季狼牙算法组招新/2022算法组考核题目中需要的图片/task_4.png",0);
    vector<vector<Point> >contours;
    vector<Vec4i>hierarchy;
    //寻找轮廓-成功存放在contours中
  findContours(srcImg,contours,hierarchy,RETR_CCOMP,CHAIN_APPROX_SIMPLE);
    Mat l=Mat::zeros(srcImg.rows,srcImg.cols,CV_8UC3);//C3三个通道才能画出蓝色,C1只能画出黑白
    // for(int i=0;i<contours.size();++i)
    //     drawContours(line,contours,i,Scalar(255,0,0));//用蓝线把轮廓描绘出来
    //imshow("result",line);
    int max=0;
    double maxArea=0;
    for(int i=0;i<contours.size();++i){
      double area=contourArea(contours[i]);
      if(area>maxArea){
        max=i;
        maxArea=area;
      }
    }//找到了最大的轮廓
    drawContours(l,contours,max,Scalar(255,0,0),2);
    imshow("max",l);
    waitKey(0);
    RotatedRect ROI=minAreaRect(contours[max]);
    
    //rectangle(line,ROI,Scalar(0,255,0),2);

    Point2f P[4];
    ROI.points(P);
    for(int j=0;j<4;j++){
      line(l,P[j],P[(j+1)%4],Scalar(0,0,255),2,8);
    }
    
    imshow("result",l);
    waitKey(0);
    
    imwrite("./result.png",l);
    return 0;
}
#include <iostream>
#include <stdio.h>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;
int main(int argc, char const *argv[])
{
    Mat srcImg,dstImg_warp,dstImg_rotate;
    Point2f srcTriangle[3];
    Point2f dstTriangle[3];
    Mat rotMat(2,3,CV_32FC1);
    Mat warpMat(2,3,CV_32FC1);
    
    //读取-要是完整的路径
    srcImg=imread("/Users/liujingyu/Desktop/2022赛季狼牙算法组招新/2022算法组考核题目中需要的图片/task_5.png");
    if(!srcImg.data){
        printf("读取错误\n");
    }
    imshow("init",srcImg);
    waitKey(0);
    
    dstImg_warp=Mat::zeros(srcImg.rows,srcImg.cols,srcImg.type());
    srcTriangle[0]=Point2f(0,0);
    srcTriangle[1]=Point2f(0,1);
    srcTriangle[2]=Point2f(1,0);
    //平移-设置向右平行50个单位;
    dstTriangle[0]=Point2f(50,0);
    dstTriangle[1]=Point2f(50,1);
    dstTriangle[2]=Point2f(51,0);
    warpMat=getAffineTransform(srcTriangle,dstTriangle);
    //对原图像应用仿射变换-实现了仿射变换
    warpAffine(srcImg,dstImg_warp,warpMat,dstImg_warp.size());
    imshow("平移",dstImg_warp);
    waitKey(0);
    Point center=Point(dstImg_warp.cols/2,dstImg_warp.rows/2);
    double angle=-30.0;
    rotMat=getRotationMatrix2D(center,angle,1);
    warpAffine(srcImg,dstImg_rotate,rotMat,dstImg_warp.size());
    imshow("旋转",dstImg_rotate);
    waitKey(0);

    double scale=0.7;
    rotMat=getRotationMatrix2D(center,0,scale);
    warpAffine(srcImg,dstImg_rotate,rotMat,dstImg_warp.size());
    imshow("缩放",dstImg_rotate);
    waitKey(0);

    //以下为透视变换--且是针对给出的图片来进行的
    //顺时针看-原图像的第一个点971,150,第二个点1693,245
    //第三个点1236,979,第四个点236,632
    Point2f srcpoint[4];
    Point2f dstpoint[4];
    srcpoint[0]=Point2f(971,150);
    srcpoint[1]=Point2f(1693,245);
    srcpoint[2]=Point2f(1236,979);
    srcpoint[3]=Point2f(236,632);
    dstpoint[0]=Point2f(0,0);
    dstpoint[1]=Point2f(650,0);
    dstpoint[2]=Point2f(650,900);
    dstpoint[3]=Point2f(0,900);
    Mat wrapMatrix=getPerspectiveTransform(srcpoint,dstpoint);
    Mat dstImg_pers=Mat::zeros(900,650,srcImg.type());
    warpPerspective(srcImg,dstImg_pers,wrapMatrix,dstImg_pers.size());
    imshow("透视",dstImg_pers);
    waitKey(0);
    //一个点应该是肯定的-找到轮廓中面积最大的轮廓,一般就是我们所要找的ROI区域   
    return 0;
}
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

Mat getHistImage(const MatND &hist){
    //MatND特制多维矩阵
    double maxValue=0;
    double minValue=0;
    
    minMaxLoc(hist,&minValue,&maxValue,0,0);//找出最大值和最小值
    
    int histSize=hist.rows;
    
    Mat histImage(histSize,histSize,CV_8UC3,Scalar(255,255,255));//相当于创建一个直方图的画布
    
    int hpt=static_cast<int>(0.9*histSize);
    Scalar color(172,172,150);
    for(int h=0;h<histSize;h++){
        float binVal=hist.at<float>(h);
        int intensity=static_cast<int>(binVal*hpt/maxValue);
        line(histImage,Point(h,histSize),Point(h,histSize-intensity),color);
    }
        return histImage;
}
int main(int argc, char const *argv[])
{
    Mat srcImg=imread("/Users/liujingyu/Desktop/2022赛季狼牙算法组招新/2022算法组考核题目中需要的图片/task_6.png",0);
    if(srcImg.empty()){
        cout<<"打开失败!"<<endl;
        exit(-1);
    }
    imshow("原图",srcImg);
    waitKey(0);
    Mat dst;
    equalizeHist(srcImg,dst);
    imshow("均衡化",dst);
    waitKey(0);
    //现在画直方图-现在本身就是一个灰度图
    int image_count=1;//1张图片
    int channels[1]={0};//通道0
    Mat out;
    int dims=1;
    int histsize[1]={256};//直方图横坐标的子区间数
    float hrange[2]={0,256};//区间的总范围
    
    const float *ranges[1]={hrange};//指针数组
    
    calcHist(&srcImg,image_count,channels,Mat(),out,dims,histsize,ranges);
    //计算直方图
    
    Mat last=getHistImage(out);//画直方图的图像
    
    imshow("result",last);
    waitKey(0);
    
    Mat s=imread("/Users/liujingyu/Desktop/视觉学习/u=2108319215,1494231136&fm=253&fmt=auto&app=120&f=JPEG.jpg");
    imshow("BGR",s);
    waitKey(0);
    cvtColor(s,s,COLOR_BGR2HSV);
    imshow("HSV",s);
    waitKey(0);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值