opencv 学习: 10 图像的简单运算

图片可以进行组合计算,因为图片可以被看作是矩阵;矩阵可以进行加减乘除的四则运算。

1.加法计算,以叠加两张图片为例

在向图片添加一些效果,或者叠加一些信息时,可以将两张图片叠加。

可以调用 cv::add 或者 cv::addWeighted 方法将两张图片叠加,后者拥有权重参数,来决定两张图片叠加时,各自的权重

cv::add(image1, image2, result);
cv::addWeighted(image1, 0.5, image2, 0.5, 0. , result);

这些算数计算方法,默认内部都使用了 cv::saturate_cast 方法来确保计算结果的像素值,处于合法值域范围内。

可以将输入的图片之一,作为结果参数。可以避免 result 做一次内存分配。

示例代码:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <chrono>

int main(int argc, char *argv[])
{

    // 检查命令行参数

    if (argc != 4)
    {
        std::cerr << "Usage: " << argv[0] << " <input_image1> <input_image2> <output_image>" << std::endl;
        return -1;
    }

    // 读取输入图像和logo图像
    cv::Mat input_image1 = cv::imread(argv[1]);

    // 检查输入图像和logo图像是否成功读取
    if (input_image1.empty())
    {
        std::cerr << "Error: Could not open or find input image" << std::endl;
    }

    // 读取输入图像和logo图像
    cv::Mat input_image2 = cv::imread(argv[2]);

    // 检查输入图像和logo图像是否成功读取
    if (input_image2.empty())
    {
        std::cerr << "Error: Could not open or find input image" << std::endl;
    }

    cv::namedWindow("input_image1", cv::WINDOW_NORMAL);
    cv::imshow("input_image1", input_image1);
    cv::namedWindow("input_image2", cv::WINDOW_NORMAL);
    cv::imshow("input_image2", input_image2);
    cv::waitKey(0);

    cv::Mat result;
    const int64 start = cv::getTickCount();
    //注意,需要图片尺寸格式一只,也就是行列数,通道数一致。

#if 0 //[1]
    //result[i] = input_image1[i] + input_image2[i]
    //cv::add(input_image1, input_image2, result);
#endif

#if 0 //[2]
    //result[i] = input_image1[i] * 0.5 + input_image2[i] * 0.5 + 0
    cv::addWeighted(input_image1, 0.5, input_image2, 0.5, 0, result);
#endif

#if 0 //[3]
    //result[i] = input_image1[i] + 50
    cv::add(input_image1, cv::Scalar(50), result);
#endif

#if 0 //[4]
    //result[i] = input_image1[i] * 0.5 + input_image2[i]
    cv::scaleAdd(input_image1, 0.5, input_image2, result);
#endif

#if 0 //[5.1]
    //mask-8位单通道数组,指定要更改的输出数组的元素
    cv::Mat mask = cv::Mat::zeros(input_image1.size(), CV_8UC1);
    //上半截参与计算
    mask(cv::Rect(0, 0, mask.cols, mask.rows / 2)).setTo(cv::Scalar(255));
    //if(mask[i] != 0) result[i] = input_image1[i] + input_image2[i]
    cv::add(input_image1, input_image2, result, mask);
#endif


#if 1 //[5.2]
    //mask-8位单通道数组,指定要更改的输出数组的元素
    cv::Mat mask = cv::Mat::zeros(input_image1.size(), CV_8UC1);
    //中部1/3参与计算
    mask(cv::Rect(0, mask.rows / 3, mask.cols, mask.rows / 3)).setTo(cv::Scalar(255));
    //使用图片二作为背景 ,这里 result是引用的input_image2
    result = input_image2;
    //if(mask[i] != 0) result[i] = input_image1[i] + input_image2[i]
    cv::add(input_image1, input_image2, result, mask);
#endif
 
    const int64 end = cv::getTickCount();
    std::cout << "sharpen used time: " << (end - start) / cv::getTickFrequency() << "s" << std::endl;

    cv::imwrite(argv[3], result);
    cv::namedWindow("result", cv::WINDOW_NORMAL);
    cv::imshow("result", result);
    cv::waitKey(0);

    return 0;
}

准备两张图片,进行叠加结果如下:

[2] addWeighted
在这里插入图片描述

注意,需要图片尺寸格式一只,也就是行列数,通道数一致。

否则,会报异常:

terminate called after throwing an instance of ‘cv::Exception’
what(): OpenCV(4.12.0) /mnt/c/mine/largescalecppv1/image_processing/opencv-4.12.0/modules/core/src/arithm.cpp:662: error: (-209:Sizes of input arguments do not match) The operation is neither ‘array op array’ (where arrays have the same size and the same number of channels), nor ‘array op scalar’, nor ‘scalar op array’ in function ‘arithm_op’

Aborted (core dumped)

[5.1] add 使用mask, 只计算上半截
在这里插入图片描述

[5.2] add 使用mask, 只计算中间2/3截,并且将 image2作为结果图片。
在这里插入图片描述

2.其它计算方法

OpenCV提供大量的计算方法。

cv::subtract
cv::absdiff
cv::multiply
cv::divide

Bitwise operators
cv::bitwise_and
cv::bitwise_or
cv::bitwise_xor
cv::bitwise_not

cv::min
cv::max

cv::sqrt
cv::pow
cv::abs
cv::exp
cv::log

示例代码:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <chrono>

//图片相减,特别示例mask用法
void subtract_demo(const cv::Mat &input_image1, const cv::Mat &input_image2, cv::Mat &result)
{
    //mask-8位单通道数组,指定要更改的输出数组的元素
    cv::Mat mask = cv::Mat::zeros(input_image1.size(), CV_8UC1);
    //中部1/3参与计算
    mask(cv::Rect(0, mask.rows / 3, mask.cols, mask.rows / 3)).setTo(cv::Scalar(255));
    //使用图片二作为背景
    result = input_image2;

    //if(mask[i] != 0) result[i] = input_image1[i] + input_image2[i]
    cv::subtract(input_image1, input_image2, result, mask);
}

//计算两个图片之间各个像素的绝对差值
//实质是,计算两个数组之间或数组与标量之间的每个元素的绝对差。
void absdiff_demo(const cv::Mat &input_image1, const cv::Mat &input_image2, cv::Mat &result)
{
    cv::absdiff(input_image1, input_image2, result);
}

//计算两个数组的按元素缩放的乘积。
void multiply_demo(const cv::Mat &input_image1, const cv::Mat &input_image2, cv::Mat &result)
{
    //缩放因子 0.01 不然会溢出,全 255, 全白了
    cv::multiply(input_image1, input_image2, result);
    cv::multiply(input_image1, input_image2, result, 0.01);
}
/*
  
  
    cv::divide

    //Bitwise operators
    cv::bitwise_and
    cv::bitwise_or
    cv::bitwise_xor
    cv::bitwise_not

    cv::min
    cv::max

    cv::sqrt
    cv::pow
    cv::abs
    cv::exp
    cv::log
*/
int main(int argc, char *argv[])
{

    // 检查命令行参数

    if (argc != 4)
    {
        std::cerr << "Usage: " << argv[0] << " <input_image1> <input_image2> <output_image>" << std::endl;
        return -1;
    }

    // 读取输入图像和logo图像
    cv::Mat input_image1 = cv::imread(argv[1]);

    // 检查输入图像和logo图像是否成功读取
    if (input_image1.empty())
    {
        std::cerr << "Error: Could not open or find input image" << std::endl;
    }

    // 读取输入图像和logo图像
    cv::Mat input_image2 = cv::imread(argv[2]);

    // 检查输入图像和logo图像是否成功读取
    if (input_image2.empty())
    {
        std::cerr << "Error: Could not open or find input image" << std::endl;
    }

    cv::namedWindow("input_image1", cv::WINDOW_NORMAL);
    cv::imshow("input_image1", input_image1);
    cv::namedWindow("input_image2", cv::WINDOW_NORMAL);
    cv::imshow("input_image2", input_image2);
    cv::waitKey(0);

    cv::Mat result;
    const int64 start = cv::getTickCount();
    //注意,需要图片尺寸格式一只,也就是行列数,通道数一致。

    //subtract_demo(input_image1,input_image2,result);
    //absdiff_demo(input_image1,input_image2,result);
    multiply_demo(input_image1,input_image2,result);
  
    const int64 end = cv::getTickCount();
    std::cout << "sharpen used time: " << (end - start) / cv::getTickFrequency() << "s" << std::endl;

    cv::imwrite(argv[3], result);
    cv::namedWindow("result", cv::WINDOW_NORMAL);
    cv::imshow("result", result);
    cv::waitKey(0);

    return 0;
}

subtract_demo 结果:

高亮像素值减暗像素值,会更多的保留原始颜色。

暗像素值减比它更亮的值,会变成负数,然后被归为 0 ,也就是还是暗的。

颜色像素值接近,减完会变成暗色。

在这里插入图片描述

absdiff_demo 结果:

计算每个像素间的绝对差值。

也就是两个图片像素的颜色值越相近,结果就越暗。

越亮,或者最终结果保留了亮部的结果的,就是这个像素值位置,是一明一暗的状态,最终计算绝对差值,更接近亮值,所以亮值的信息存留了下来。

中部的天空。最右侧的绿色骆驼。

中部的天空和太阳都处于两张图片的亮部,颜色亮度上差值较小,所以最终看起来更黑。

在这里插入图片描述

multiply_demo 结果

每个像素相乘,亮的更亮,暗的,特别是黑色的部分就是黑洞,乘完还是零,所以保留了黑色。

最后的结果还挺好玩的。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值