OpenCV(C++)图像运算

Posted 计算机视觉与机器学习

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV(C++)图像运算相关的知识,希望对你有一定的参考价值。

图像在计算机中就是一个普通的数值矩阵存在的,所以也就能够相应的进行各种运算,这些运算构成了图像处理的基本操作。图像加法可以混合两幅图像进行图像融合,比如在处理照片的贴图;图像减法可以用来去掉运动图像的背景,来进行目标定位追踪。这篇文章主要介绍图像的算术运算,逻辑运算,重映射变换等。

代数运算

算术运算包括加、减、乘、除和位运算,这些运算操作的特点是提供两个输入参数,得到一个输出结果。有时候还可以运算操作的权重系数,或者指定掩码。

加法运算拥有多种格式,能够灵活的进行各种不同的加法操作。下面主要介绍几种加法运算的用法。

最普通的加法操作 c[i] = a[i] + b[i] 。

 
   
   
 
  1. cv::add(imageA,imageB,resultC);

将图像加上一个常数 c[i] = a[i] + k, 注意传入的Scalar是表示颜色的对象,需要根据图像的通道数来具体定义。

 
   
   
 
  1. //灰度图像

  2. cv::add(imageA,cv::Scalar(k),resultC);    

  3. //彩色图像

  4. cv::add(imageA,cv::Scalar(k,i,j),resultC);    

将两幅图像进行加权混合 c[i] = k1*a[i] + k2*b[i] + k3 。

 
   
   
 
  1. cv::addWeighted(imageA,k1,imageB,k2,k3,resultC);

还能进行线性相加 c[i] = k*a[i] + b[i]。

 
   
   
 
  1. cv::scaleAdd(imageA,k,imageB,resultC);

这些都是图像整体的加法运算,还能够添加一个掩码参数,掩码的作用就是让图像的某一部分参与运算,其他部分保持原样。具体来说,掩码通常与要运算的图像大小一样,当掩码的值非空(即为真),则在对应的图像位置进行运算,否则不运算。

 
   
   
 
  1. cv::add(imageA,imageB,resultC,mask);

除了加法运算外,还有减法 cv::subtract、乘法 cv::multiply、除法 cv::divede、差的绝对值cv::absdiff,这些函数也有多种格式。

位运算符也是常用的一类运算符,如与运算 cv::bitwise_and、或运算cv::bitwise_or、异或运算 cv::bitwise_xor、非运算 cv::bitwise_not。cv::max 和 cv::min 能够找到两幅图像中的最大或者最小值。 此外还能对单张图像进行数学操作,如 cv::sqrt、cv::pow、cv::abs、cv::exp等等。

值得注意的一点是,图像像素的值范围通常是0-255,所以经过这些操作之后,有可能值会超出这个范围,然后会进行补码运算使值在unsigned char 能表达的范围之内,会造成结果错误的情况。所以记得使用 cv::saturate_cast 函数,这个函数能够把小于0的调整为0,大于255的调整为255,把浮点数调整为最接近的整数,以确保结果在预定的像素范围之内。

重载运算符

大部分的C++中的运算符在Opencv中都进行了重载,所以进行算术运算符时可以直接使用运算符。例如+、 -、 *、 / 和 &、 | 、^ 、~ 都能够直接使用,而比较运算符<、>、==、>=等也进行了重载,运算结果会返回一个8 bit 的二值图像。

 
   
   
 
  1. C = 4 * A + 7 * B + 8;

  2. C = A >= B;

图像还是一个矩阵,所以矩阵的操作也进行了重载。矩阵乘法A*B,矩阵求逆 A.inv(),矩阵转置 A.t(),求矩阵行列式 A.determinant(),叉乘 A.cross(B),点乘 A.dot(B)。此外,所有复合赋值运算符+=、-=、&=也是可以使用的。

图像通道分割

有时候需要对多通道的图像进行通道分割,对不同的通道进行不同的操作。这时候就可以使用 cv::split 函数,将图像的三个通道分别放到三个Mat对象中。而把三个通道合并可以使用 cv::merge 函数,即合并成一彩色图像。例如把一张图像与蓝色通道进行混合,可以这样实现:

 
   
   
 
  1. std::vector<cv::Mat> planes;

  2. cv::split(image,planes);

  3. planes[0] += image2;

  4. cv.merge(planes,result);

注意:在Opencv中通道顺序是BGR,蓝色表示channels[0],绿色表示channels[1],红色表示channels[2]。

图像重映射

前面都是改变图像的像素值,如果只想改变像素的位置,可以使用 remap 函数,只需要定义好映射参数,然后将映射参数应用到输入图像上就可以。映射参数分为 X 坐标轴 和 Y 坐标轴,它们都用浮点数类型的 cv::Mat 表示,然后根据某种规则创建映射参数。下面展示一段用remap函数反转图像的代码(虽然已有方法 flip() 可以直接做到) :

 
   
   
 
  1. using namespace cv;

  2. int main()

  3. {

  4.    //变量定义

  5.    Mat srcImage, dstImage;

  6.    Mat map_x, map_y;

  7.    //载入原始图

  8.    srcImage = imread("E:\\写作插图\\下载.jpg", 1);

  9.    if (!srcImage.data)

  10.    {

  11.        printf("error\n");

  12.        return false;

  13.    }

  14.    imshow("原始图", srcImage);

  15.    //创建和原始图一样的效果图,x重映射图,y重映射图

  16.    dstImage.create(srcImage.size(), srcImage.type());

  17.    map_x.create(srcImage.size(), CV_32FC1);

  18.    map_y.create(srcImage.size(), CV_32FC1);

  19.    //双层循环,创建映射参数

  20.    for (int j = 0; j < srcImage.rows; j++)

  21.    {

  22.        for (int i = 0; i < srcImage.cols; i++)

  23.        {

  24.            //改变map_x & map_y的值.

  25.            map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);

  26.            map_y.at<float>(j, i) = static_cast<float>(j);

  27.        }

  28.    }

  29.    //进行重映射操作

  30.    remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

  31.    //显示效果

  32.    imshow("映射图像", dstImage);

  33.    waitKey(0);

  34.    return 0;

  35. }

值得注意的是,映射参数是浮点数,所以可以映射到一个非整数值,因此必须进行插值操作,常用的插值操作有INTER_NEAREST 最近邻插值 ,INTER_LINEAR 双线性插值(默认值),INTER_CUBIC 双三次样条插值,这里使用的就是INTER_LINEAR插值方法。

原始图与映射图片的对比


以上是关于OpenCV(C++)图像运算的主要内容,如果未能解决你的问题,请参考以下文章

图像的膨胀与腐蚀——OpenCV与C++的具体实现

基于OpenCV实现的图像油画效果代码(高效率低耗时的C++代码-带详细注释)

OpenCV C++ Mat == 运算符

OpenCV实战——图像运算详解

OpenCV实战——图像运算详解

OpenCV车辆识别 C++ OpenCV 原理介绍 + 案例实现