有些时候,相机会因为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);