一、线性混合操作

void cv::addWeighted    (   InputArray  src1,
                            double  alpha,
                            InputArray  src2,
                            double  beta,
                            double  gamma,
                            OutputArray     dst,
                            int     dtype = -1 
)
  • 计算两个数组的加权和
  • 这两个数组的加权和的关系可有下式表示
    dst(I)=saturate(src1(I)∗alpha+src2(I)∗beta+gamma) 用数学的方式表示 dst(I)=saturate(src1(I)∗α+src2(I)∗β+γ)
    其中I是一个多维数组索引。在计算多维数组索引的情况下,每一个通道值都是单独计算的。这个函数可以用矩阵表达式代替
    dst = src1*alpha+src2*beta+gamma
    α,β是两图在新图中所占的比例,一般的α+β=1,当然也可以不等于1。
    γ为一个增益变量,作用是可以提高或减小整体的亮度。
  • 最后一个参数dtype的作用是指定输出数组的类型;当两个输入数组是相同的depth时,dtype被设置成-1,也就是输出数组的类型与src1.depth()相等。
  • 相似的函数:
    add, subtract, scaleAdd, Mat::convertTo
  • nots:
  1. 输出数组的深度不能是CV_32S,如果是的话,你可能得到不正确的结果。
  2. 两个输入数组的类型必须相同。但是类型相同,主要就是说通道数相同;经过如下情况亦可
    第一种情况
cvtColor(src2, src2, COLOR_BGR2GRAY);   
cvtColor(src2, src2, COLOR_GRAY2BGR);
//得到的src2能够使用

第二种情况

src4.convertTo(src2, CV_16UC3);
//src4亦可使用

用处:视频字幕上可以看成是这样操作的

使用方法

//【1】API方式:
//addWeighted(src1, 2, src2, 2, -60, dst);

//【2】表达式方式
//dst = src1*2 + src1*2 - 60;

//【3】遍历图像的方式
    Mat_<Vec3b> src1_data = src1;
    Mat_<Vec3b> src2_data = src2;
    Mat_<Vec3b> dst_data = dst;

    for (int i = 0; i < src1.rows; i++)
    {
        for (int j = 0; j < src1.cols; j++)
        {
            dst_data(i, j)[0] = saturate_cast<uchar>(src1_data(i, j)[0] * 2 -src2_data(i, j)[0] * 2- 60);
            dst_data(i, j)[1] = saturate_cast<uchar>(src1_data(i, j)[1] * 2 -src2_data(i, j)[0] * 2- 60);
            dst_data(i, j)[2] = saturate_cast<uchar>(sr1c_data(i, j)[2] * 2 -src2_data(i, j)[0] * 2- 60);
        }
    }

程序文件

二、调整图像的亮度与对比度

图像变换可以分为两类操作:

  • 像素变换-点操作
  • 邻域操作-区域
    线性混合、调整图像的亮度与对比度属于像素变换。卷积操作,图形操作属于邻域操作;

调整图像的亮度与对比度原理:


dst(I)=saturate(src1(I)∗alpha+gamma)

用数学的方式表示


dst(I)=saturate(src1(I)∗α+γ)


γ是增益变量同同上边的。 β=0

这个暂时不知道它有没有独有的API,但是可以通过上面线性混合的API得到两种,只需要把两个输入图像中的一个图像的比例设置为0,或者传入一个所有像素值为0的空图像,即可。


当然可以用矩阵的运算

dst = src1*alpha+src2*0+gamma


使用方法

//【1】API方式:
    addWeighted(src1, 2, src2, 0, -60, dst);

    //【2】表达式方式
    dst = src1*2 + 60;

    //【3】遍历图像的方式
    Mat_<Vec3b> src_data = src1;
    Mat_<Vec3b> dst_data = dst;

    //Mat_<Vec3b> dst1_data = dst1;
    for (int i = 0; i < src1.rows; i++)
    {
        for (int j = 0; j < src1.cols; j++)
        {
            dst_data(i, j)[0] = saturate_cast<uchar>(src_data(i, j)[0] * 2 - 60);
            dst_data(i, j)[1] = saturate_cast<uchar>(src_data(i, j)[1] * 2 - 60);
            dst_data(i, j)[2] = saturate_cast<uchar>(src_data(i, j)[2] * 2 - 60);

            //这种方式是错误的,寻找错误方法见程序文件
            /* 比如说点(50,300)的值为26,经过下式计算应该得-8,经过saturate计算的为0,没有经过saturate转换的确成了228*/
            /*dst1_data(i, j)[0] = (uchar)(src_data(i, j)[0] * 1.2 - 60);
            dst1_data(i, j)[1] = (uchar)(src_data(i, j)[1] * 1.2 - 60);
            dst1_data(i, j)[2] = (uchar)(src_data(i, j)[2] * 1.2 - 60);*/
        }
    }

程序文件


注意:

这种对点的操作都需要把计算结果的值经过saturate_cast转换,不然容易溢出,得到不是想得到的值。