美文网首页计算机视觉
OpenCV(C++)图像运算

OpenCV(C++)图像运算

作者: 有苦向瓜诉说 | 来源:发表于2018-12-08 20:27 被阅读22次

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

    代数运算

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

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

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

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

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

    //灰度图像
    cv::add(imageA,cv::Scalar(k),resultC);    
    //彩色图像
    cv::add(imageA,cv::Scalar(k,i,j),resultC);    
    

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

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

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

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

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

    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 的二值图像。

    C = 4 * A + 7 * B + 8;
    C = A >= B;
    

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

    图像通道分割

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

    std::vector<cv::Mat> planes;
    cv::split(image,planes);
    planes[0] += image2;
    cv.merge(planes,result);
    

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

    图像重映射

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

    using namespace cv;
    int main()
    {
        //变量定义
        Mat srcImage, dstImage;
        Mat map_x, map_y;
        //载入原始图
        srcImage = imread("E:\\写作插图\\下载.jpg", 1);
        if (!srcImage.data) 
        { 
            printf("error\n"); 
            return false; 
        }
        imshow("原始图", srcImage);
        //创建和原始图一样的效果图,x重映射图,y重映射图
        dstImage.create(srcImage.size(), srcImage.type());
        map_x.create(srcImage.size(), CV_32FC1);
        map_y.create(srcImage.size(), CV_32FC1);
        //双层循环,创建映射参数
        for (int j = 0; j < srcImage.rows; j++)
        {
            for (int i = 0; i < srcImage.cols; i++)
            {
                //改变map_x & map_y的值.
                map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);
                map_y.at<float>(j, i) = static_cast<float>(j);
            }
        }
        //进行重映射操作
        remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
        //显示效果
        imshow("映射图像", dstImage);
        waitKey(0);
        return 0;
    }
    

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

    原始图与映射图片的对比


    欢迎大家关注公众号“计算机视觉与机器学习”


    计算机视觉和机器学习

    相关文章

      网友评论

        本文标题:OpenCV(C++)图像运算

        本文链接:https://www.haomeiwen.com/subject/ggtchqtx.html