Opencv-C++笔记 (10) : opencv-图像像素计算

一、概率

我们可以将数字图像理解成一定尺寸的矩阵,矩阵中每个元素的大小表示了图像中每个像素的亮暗程度,因此统计矩阵中的最大值,就是寻找图像中灰度值最大的像素,计算平均值就是计算图像像素平均灰度,可以用来表示图像整体的亮暗程度。因此针对矩阵数据的统计工作在图像像素中同样具有一定的意义和作用。在OpenCV 4中集成了求取图像像素最大值、最小值、平均值、均方差等众多统计量的函数,接下来将详细介绍这些功能的相关函数。

二、寻找图像像素的最大值和最小值

void cv::minMaxLoc(InputArray src,
                   double * minVal,
                   double * maxVal = 0,
                   Point * minLoc = 0,
                   Point * maxLoc = 0,
                   InputArray mask = noArray())

src:需要寻找最大值和最小值的图像或者矩阵,要求必须是单通道矩阵
minVal:图像或者矩阵中的最小值。
maxVal:图像或者矩阵中的最大值。
minLoc:图像或者矩阵中的最小值在矩阵中的坐标。
maxLoc:图像或者矩阵中的最大值在矩阵中的坐标。
mask:掩模,用于设置在图像或矩阵中的指定区域寻找最值。

这里我们见到了一个新的数据类型Point,该数据类型是用于表示图像的像素坐标,由于图像的像素坐标轴以左上角为坐标原点,水平方向为x轴,垂直方向为y轴,因此Point(x,y)对应于图像的行和列表示为Point(列数,行数)。在OpenCV中对于2D坐标和3D坐标都设置了多种数据类型,针对2D坐标数据类型定义了整型坐标cv::Point2i(或者cv::Point)、double型坐标cv::Point2d、浮点型坐标cv::Point2f,对于3D坐标同样定义了上述的坐标数据类型,只需要将其中的数字“2”变成“3”即可。对于坐标中x、y、z轴的具体数据,可以通过变量的x、y、z属性进行访问,例如Point.x可以读取坐标的x轴数据。

该函数实现的功能是寻找图像中特定区域内的最值,函数第一个参数是输入单通道矩阵,需要注意的是,该变量必须是一个单通道的矩阵数据,如果是多通道的矩阵数据,需要用cv::Mat::reshape()将多通道变成单通道,或者分别寻找每个通道的最值,然后再进行比较寻找到全局最值。对于cv::Mat::reshape()的用法,在代码清单3-8中给出。第二到第五个参数分别是指向最小值、最大值、最小值位置和最大值位置的指针,如果不需要寻找某一个参数,可以将该参数设置为NULL,函数最后一个参数是寻找最值得掩码矩阵,用于标记寻找上述四个值的范围,参数默认值为noArray(),表示寻找范围是矩阵中所有数据。

Mat cv::Mat::reshape(int  cn,int  rows = 0)

cn:转换后矩阵的通道数。
rows:转换后矩阵的行数,如果参数为零,则转换后行数与转换前相同。

注意
如果矩阵中存在多个最大值或者最小值时,minMaxLoc()函数输出最值的位置为按行扫描从左向右第一次检测到最值的位置,同时输入参数时一定要注意添加取地址符。
为了让读者更加了解minMaxLoc()函数的原理和使用方法,在代码清单3-9中给出寻找矩阵最值的示例程序,在图3-6中给出了程序运行的最终结果,在图3-7给出了创建的两个矩阵和通道变换后的矩阵在Image
Watch中查看的内容。

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    
    
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    float a[12]={
    
    1,2,3,4,5,10,6,7,8,9,10,0};
    Mat img=Mat(3,4,CV_32FC1,a);//单通道矩阵
    Mat imgs=Mat(2,3,CV_32FC2,a);//多通道矩阵
    double minVal,maxVal;//用于存放矩阵中的最大值和最小值
    Point minIdx,maxIdx;//用于存放矩阵中的最大值和最小值的位置

    /*寻找单通道矩阵中的最值*/
    minMaxLoc(img,&maxVal,&minVal,&minIdx,&maxIdx);
    cout << "img中最大值是:" << maxVal << " " << "在矩阵中的位置:" << maxIdx << endl;
    cout << "img中最小值是:" << minVal << " " << "在矩阵中的位置:" << minIdx << endl;

    /*寻找多通道矩阵中的最值*/
    Mat imgs_re=imgs.reshape(1,4);//将多通道矩阵变成单通道矩阵
    minMaxLoc(imgs_re,&minVal,&maxVal,&minIdx,&maxIdx);
    cout << "img中最大值是:" << maxVal << " " << "在矩阵中的位置:" << maxIdx << endl;
    cout << "img中最小值是:" << minVal << " " << "在矩阵中的位置:" << minIdx << endl;
    return 0;
}

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

三、计算图像的均值和标准方差

图像的均值表示图像整体的亮暗程度,图像的均值越大图像整体越亮。标准方差表示图像中明暗变化的对比程度,标准差越大表示图像中明暗变化越明显。OpenCV
4提供了mean()函数用于计算图像的平均值,提供了meanStdDev()函数用于同时计算图像的均值和标准方差。接下来将详细的介绍这两个函数的使用方法。

cv::Scalar cv::mean(InputArray src,InputArray mask = noArray())

src:待求平均值的图像矩阵。
mask:掩模,用于标记求取哪些区域的平均值。

该函数用来求取图像矩阵的每个通道的平均值,函数的第一个参数用来输入待求平均值的图像矩阵,其通道数目可以在1到4之间。需要注意的是,该函数的返回值是一个cv::Scalar类型的变量,函数的返回值有4位,分别表示输入图像4个通道的平均值,如果输入图像只有1个通道,那么返回值的后三位都为0,例如输入该函数一个单通道平均值为1的图像,输出的结果为[1,0,0,0],可以通过cv::Scalar[n]查看第n个通道的平均值。该函数的第二个参数用于控制图像求取均值的范围,在第一个参数中去除第二个参数中像素值为0的像素,计算的原理如式(3.5)所示,当不输入第二个参数时,表示求取第一个参数全部像素的平均值。
在这里插入图片描述
其中表示第c个通道的平均值,表示第c个通道像素的灰度值。
meanStdDev()函数可以同时求取图像每个通道的平均值和标准方差,其函数原型在代码清单3-11中给出。

void cv::meanStdDev(InputArray src,OutputArray mean,OutputArray stddev,InputArray mask = noArray())
src:待求平均值的图像矩阵。
mean:图像每个通道的平均值,参数为Mat类型变量。
stddev:图像每个通道的标准方差,参数为Mat类型变量。
mask:掩模,用于标记求取哪些区域的平均值和标准方差。

该函数的第一个参数与前面mean()函数第一个参数相同,都可以是1-4通道的图像,不同之处在于该函数没有返回值,图像的均值和标准方差输出在函数的第二个和第三个参数中,区别于mean()函数,用于存放平均值和标准方差的是Mat类型变量,变量中的数据个数与第一个参数通道数相同,如果输入图像只有一个通道,该函数求取的平均值和标准方差变量中只有一个数据。该函数计算原理如式(3.6)所示。

在这里插入图片描述

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    
    
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    float a[12]={
    
    1,2,3,4,5,10,6,7,8,9,10,0};
    Mat img=Mat(3,4,CV_32FC1,a);//单通道矩阵
    Mat imgs=Mat(2,3,CV_32FC2,a);//多通道矩阵

    cout << "/* 用meanStdDev同时求取图像的均值和标准方差 */" << endl;
    Scalar myMean;
    myMean=mean(imgs);
    cout<<"imgs均值 = "<<myMean<<endl;
    cout<<"imgs第一个通道的均值 = "<<myMean[0]<<" "
        <<"imgs第二个通道的均值 = "<<myMean[1]<<endl;

    cout << "/* 用meanStdDev同时求取图像的均值和标准方差 */" << endl;
    Mat myMeanMat,myStddevMat;

    meanStdDev(img,myMeanMat,myStddevMat);
    cout << "img均值=" << myMeanMat << " " << endl;
    cout << "img标准方差=" << myStddevMat << endl << endl;
    meanStdDev(imgs,myMeanMat,myStddevMat);
    cout << "img均值=" << myMeanMat << " " << endl;
    cout << "img标准方差=" << myStddevMat << endl << endl;
    return 0;
}

在这里插入图片描述

4、两张图片的比较运算

OpenCV 4中提供了求取两张图像每一位像素较大或者较小灰度值的max()、min()函数,这两个函数分别比较两个图像中每一位元素灰度值的大小,保留较大(较小)的灰度值

 void cv::max(InputArray src1,InputArray src2,OutputArray dst) void
 cv::min(InputArray src1,InputArray src2,OutputArray dst)
  • src1:第一个图像矩阵,可以是任意通道数的矩阵。
  • src2:第二个图像矩阵,尺寸和通道数以及数据类型都需要与src1一致。
  • dst:保留对应位置较大(较小)灰度值后的图像矩阵,尺寸、通道数和数据类型与src1一致。

该函数的功能相对来说比较简单,就是比较图像每个像素的大小,按要求保留较大值或者较小值,最后生成新的图像。例如,第一张图像位置像素值为100,第二张图像位置像素值为10,那么输出图像位置像素值为100。在代码清单3-14中给出了这两个函数的代码实现过程以及运算结果,运算结果在图3-9、图3-10和图3-11中给出。这种比较运算主要用在对矩阵类型数据的处理,与掩模图像进行比较运算可以实现抠图或者选择通道的效果。

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    
    
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    float a[12]={
    
    1,2,3.3,4,5,9,5,7,8.2,9,10,2};
    float b[12]={
    
    1,2.2,3,1,3,10,6,7,8,9.3,10,1};
    Mat imga=Mat(3,4,CV_32FC1,a);
    Mat imgb=Mat(3,4,CV_32FC1,b);
    Mat imgas=Mat(2,3,CV_32FC2,a);
    Mat imgbs=Mat(2,3,CV_32FC2,b);

    //对两个单通道矩阵进行比较远算
    Mat myMax,myMin;
    max(imga,imgb,myMax);
    min(imga,imgb,myMin);

    //对两个多通道矩阵进行比较运算
    Mat myMaxs,myMins;
    max(imgas,imgbs,myMaxs);
    min(imgas,imgbs,myMins);

    //对两张彩色图像进行比较运算
    Mat img0=imread("lena.png");
    Mat img1=imread("noobcv.jpg");
    if(img0.empty()||img1.empty()){
    
    
        cout<<"请确认图像文件名称是否正确"<<endl;
        return -1;
    }
    Mat comMin,comMax;
    max(img0,img1,comMax);
    min(img0,img1,comMin);
    imshow("comMin",comMin);
    imshow("comMax",comMax);

    //与掩模进行比较运算
    Mat src1=Mat::zeros(Size(512,512),CV_8UC3);
    Rect rect(100,100,300,300);
    src1(rect)=Scalar(255,255,255);//生成一个300*300的掩模
    Mat comsrc1,comsrc2;
    min(img0,src1,comsrc1);
    imshow("comsrc1",comsrc1);

    Mat src2=Mat(512,512,CV_8UC3,Scalar(0,0,255));//生成一个显示红色的低通掩模
    min(img0,src2,comsrc2);
    imshow("comsrc2",comsrc2);

    //对两张图片灰度图像进行比较运算
    Mat img0G,img1G,comMinG,comMaxG;
    cvtColor(img0,img0G,COLOR_BGR2GRAY);
    cvtColor(img1,img1G,COLOR_BGR2GRAY);
    max(img0G,img1G,comMaxG);
    max(img0G,img1G,comMaxG);
    imshow("comMinG",comMinG);
    imshow("comMaxG",comMaxG);
    return 0;
}

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

5、两张图片的逻辑运算

OpenCV 4针对两个图像像素之间的与、或、异或以及非运算提供了**bitwise_and()、bitwise_or()、bitwise_xor()和bitwise_not()**四个函数,在代码清单3-15中给出了这四个函数的函数原型。在了解函数用法之前,我们先了解一下图像像素逻辑运算的规则。图像像素间的逻辑运算与数字间的逻辑运算相同,具体规则在图3-12中给出。像素的非运算只能针对一个数值进行,因此在图3-12中对像素求非运算时对图像1的像素值进行非运算。如果像素取值只有0和1的话,那么图中的前4行数据正好对应了所有的运算规则,但是CV_8U类型的图像像素值从0取到255,此时的逻辑运算就需要将像素值转成二进制数后再进行,因为CV_8U类型是8位数据,因此对0求非是11111111,也就是255。在图3-12中最后一行数据中,像素值5对应的二进制为101,像素值6对应的二进制是110,因此与运算得100(4),或运算得111(7),异或运算得011(3),对像素值5进行非运算得11111010(250)。了解了像素的辑运算原理之后,我们再来看OpenCV
4中提供的辑运算函数的使用方法。
在这里插入图片描述

//像素求与运算
  void cv::bitwise_and(InputArray src1,
                           InputArray src2,
                           OutputArray dst,
                          InputArray mask = noArray()
                            )
  //像素求或运算
   void cv::bitwise_or(InputArray src1,
                     InputArray src2,
                      OutputArray dst,
                      InputArray mask = noArray()
                        )
   //像素求异或运算
   void cv::bitwise_xor(InputArray src1,
                       InputArray src2,
                       OutputArray dst,
                       InputArray mask = noArray()
                        )
   //像素求非运算
   void cv::bitwise_not(InputArray src,
                       OutputArray dst,
                       InputArray mask = noArray()
                         )
  • src1:第一个图像矩阵,可以是多通道图像数据。
  • src2:第二个图像矩阵,尺寸和通道数以及数据类型都需要与src1一致。
  • dst:逻辑运算输出结果,尺寸和通道数和数据类型与src1一致。
  • mask:掩模,用于设置图像或矩阵中逻辑运算的范围。

这几个函数都执行相应的逻辑运算,在进行逻辑计算时,一定要保证两个图像矩阵之间的尺寸、数据类型和通道数相同,多个通道进行逻辑运算时不同通道之间是独立进行的。为了更加直观的理解两个图像像素间的逻辑运算,在代码清单3-16中给出两个黑白图像像素逻辑运算的示例程序

#include <opencv2\opencv.hpp>
   2.  #include <iostream>
   3.  #include <vector>
   4.  
   5.  using namespace std;
   6.  using namespace cv;
   7.  
   8.  int main()
   9. {
    
    
   10.    Mat img = imread("lena.png");
   11.    if (img.empty())
   12.    {
    
    
   13.      cout << "请确认图像文件名称是否正确" << endl;
   14.      return -1;
   15.    }
   16.    //创建两个黑白图像
   17.    Mat img0 = Mat::zeros(200, 200, CV_8UC1);
   18.    Mat img1 = Mat::zeros(200, 200, CV_8UC1);
   19.    Rect rect0(50, 50, 100, 100);
   20.    img0(rect0) = Scalar(255);
   21.    Rect rect1(100, 100, 100, 100);
   22.    img1(rect1) = Scalar(255);
   23.    imshow("img0", img0);
   24.    imshow("img1", img1);
   25.  
   26.    //进行逻辑运算
   27.    Mat myAnd, myOr, myXor, myNot, imgNot;
   28.    bitwise_not(img0, myNot);
   29.    bitwise_and(img0, img1, myAnd);
   30.    bitwise_or(img0, img1, myOr);
   31.    bitwise_xor(img0, img1, myXor);
   32.    bitwise_not(img, imgNot);
   33.    imshow("myAnd", myAnd);
   34.    imshow("myOr", myOr);
   35.    imshow("myXor", myXor);
   36.    imshow("myNot", myNot);
   37.    imshow("img", img);
   38.    imshow("imgNot", imgNot);
   39.    waitKey(0);
   40.    return 0;
   41.  }

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jiyanghao19/article/details/131251381
今日推荐