多帧图片求平均值消除噪声

    有些时候,相机会因为ISO过高等原因,产生严重的噪声。比如下图,是用一个劣质相机拍摄的,噪声很严重。

之前在一篇论文中看到,如果是拍摄静止的图像,且相机的位置可以固定,则可以尝试拍摄多帧图像并求平均来消除这种随机噪声。于是,我就尝试了一下,效果还是很好的。下图就是上图同样角度拍摄的连续20帧图像的平均图像。

如果这样观看对比不明显,可以观看下面我用PS将两幅图拼合在一起的样子。拼合图片的左侧是拍摄的单帧图片,右侧是20幅图片平均得到的图片。明显可以感觉到,右侧的噪声少了很多。

这种方法可以有效消除噪声,缺点是相当于增长了曝光时间。对曝光时间要求不严格的情况下,可以使用这种简单的方法来获得更好地图片。

以下是使用OpenCV控制UVC摄像头拍摄图片的简单程序,里面有“常规模式”和“连续帧平均模式”可以选择。

#include <opencv2\opencv.hpp>
#include <iostream>
#include <string.h>

using namespace std;
using namespace cv;

int main()
{
	int mode = 1;			//拍照模式选择:0为常规模式,1为连续帧平均模式。

	int frame_number = 1;	//连续帧平均模式下的帧数计数
	int frame_top = 20;		//定义连续帧平均模式下的帧数上限

	int num_photo = 1;		//计算拍照总数
	int num_top = 1;		//定义需要照片的数量

	string num;		//为拍摄的照片取名字

	VideoCapture capture;	//定义一个相机
	capture.open(0);		//开启相机


	//摄像头的基本设置
	capture.set(CV_CAP_PROP_FRAME_WIDTH, 960);//像素数量设置
	capture.set(CV_CAP_PROP_FRAME_HEIGHT, 720);
	capture.set(CV_CAP_PROP_CONTRAST, 90);//对比度设置


	Mat frame;		//定义一个抓取图像的容器矩阵

	Mat average = Mat::zeros(720,960,CV_16UC3);	//定义一个用于做平均的容器,初始值置零
	Mat tmp = Mat::zeros(720, 960, CV_16UC3);	//定义一个用于暂存的容器,初始值置零

	if (mode == 1) //如果是连续帧平均模式
	{
		while (true)	//反复执行程序
		{
			capture >> frame;
			imshow("实时图像", frame);	//remember, imshow() needs a window name for its first parameter

			if (waitKey(30) >= 0)//如果有按键
			{
				average = Mat::zeros(720, 960, CV_16UC3);	//做平均的容器在每次使用时初始值置零
				for (frame_number = 1; frame_number <= frame_top; frame_number++) //累积一定数量的相邻帧图片
				{
					capture >> frame; //读取一帧新的图
					frame.convertTo(tmp, CV_16UC3); //强制类型转换,防止饱和,转换后的内容存放在暂存容器tmp中
					average = average + tmp; //将tmp累积在平均容器中
				}
				average = average / frame_top;
				
				if (num_photo < 10)
					num = "0" + to_string(num_photo);
				else
					num = to_string(num_photo);

				average.convertTo(frame, CV_8UC3);
				imwrite("image" + num + ".jpg", frame);
				num_photo++;
			}

			if (num_photo == (num_top + 1))//结束条件
				break;
		}
	}
	else  //如果是普通拍照模式
	{
		while (true)	//用来反复执行程序
		{
			capture >> frame;
			imshow("实时图像", frame);	//remember, imshow() needs a window name for its first parameter

			if (waitKey(30) >= 0)//如果有按键
			{
				if (num_photo < 10)
					num = "0" + to_string(num_photo);
				else
					num = to_string(num_photo);

				imwrite("imagea"+num+".jpg", frame);
				num_photo++;

			}
			if (num_photo == (num_top+1))//结束条件
				break;
		}
	}
	return 0;
}

请注意,连续帧平均模式中有一个求平均值的步骤,其中涉及了转换数据类型的问题。

在OpenCV中,一般的RGB图像都默认使用“CV_8UC3”的数据类型,即8位无符号3通道图像数据类型,其值域是[0,255]。但在求很多图片的平均时,需要将多幅图片累加起来,就一定会超过255这个上界,就会出现数据溢出现象,无法获得正确的结果。这时,就需要使用数据类型转换了。下面我摘录了最重要的部分代码,可以简单看一下数据类型转换函数 “.convertTo(variable_name, object_type)”的用法。其中,“CV_16UC3”数据类型的范围是“CV_16UC3”数据类型的256倍,因此只要不求超过连续256帧的和,应该都不会发生溢出问题。

Mat average = Mat::zeros(720,960,CV_16UC3);	//定义一个用于做平均的容器,初始值置零
Mat tmp = Mat::zeros(720, 960, CV_16UC3);	//定义一个用于暂存的容器,初始值置零

for (frame_number = 1; frame_number <= frame_top; frame_number++) //累积一定数量的相邻帧图片
	{
		capture >> frame; //读取一帧新的图
		frame.convertTo(tmp, CV_16UC3); //强制类型转换,防止饱和,转换后的内容存放在暂存容器tmp中
		average = average + tmp; //将tmp累积在平均容器中
	}
average = average / frame_top;

average.convertTo(frame, CV_8UC3);
imwrite("image" + num + ".jpg", frame);

 

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值