OpenCV-C++实现图片信噪比SNR及均方误差MSE的计算

本文介绍如何在OpenCV中利用Box-Muller算法生成高斯噪声,并通过计算均方误差(MSE)和信噪比(SNR)来评估图像质量。步骤包括噪声生成、误差计算和SNR的求解,适合初学者理解图像处理中的噪声模型与评价方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

  1. 读入一幅摄像头图像,记为I
  2. 向图像加入高斯噪声,噪声的灰度要和图像的灰度在一个相当的水平上,记为J
  3. 计算图像I和J的均方误差
  4. 计算图像J的信噪比

思路

1. 高斯噪声的产生

        本文是采用Box-Muller算法实现高斯分布的,而要借助Box-Muller算法产生高斯分布必须要有现成的均与分布随机数。

        由rand()函数可以产生介于0 至 RAND_MAX 的随机数。因此,若要得到[0,1]的随机数,则可使用

rand() * (1.0 / RAND_MAX)

        Box-Muller算法正是利用均匀分布产生高斯分布随机数,算法如下:

\begin{array}{l} Z = \sqrt { - 2\ln {U_1}} \cdot \sin (2\pi {U_2})\\ Z = \sqrt { - 2\ln {U_1}} \cdot \cos (2\pi {U_2}) \end{array}

        上式中的U1和U2就是两个均匀分布随机数,经过这样的一种计算之后就能产生一个Z服从均匀分布的随机数,这看起来太神奇了,不得不佩服这个算法。

        说明一下,上面的三个式子,前两个取任何一个都可以用作算法,取正弦还是余弦是无所谓的。

        另外一点,上面的式子仅仅只能产生标准的\(Z\sim N(0,1)\)高斯分布,若要产生给定的均值和方差其实也很简单。\[Y=\mu +\sigma Z\]

        上式中的\(\mu \),\(\sigma \)就是可以自己手动定义的期望和标准差(方差开根号)。

//生成高斯噪声
double generateGaussianNoise(double mu, double sigma)
{
	//定义小值
	static double z0;
	double u1, u2;
	//构造随机变量
	u1 = rand() * (1.0 / RAND_MAX);	//生成[0,1]之间的随机数
	u2 = rand() * (1.0 / RAND_MAX);	//生成[0,1]之间的随机数
	//构造高斯随机变量(Box-Muller算法)
	z0 = sqrt(-2.0*log(u1))*cos(2 * CV_PI*u2);
	return z0*sigma + mu;
}

2. 均方误差MSE的计算

{\rm{MSE}} = \frac{1}{​{MN}}\sum\limits_{m = 1}^M {\sum\limits_{n = 1}^N {​{​{\left| {I(m,n) - J(m,n)} \right|}^2}} }

//得到均方误差MSE
double getMSE(Mat & srcImage,Mat & dstImage)
{
	Mat src = dstImage;
	Mat dst = srcImage;
	int channels = dstImage.channels();
	int rowsNumber = src.rows;
	int colsNumber = src.cols*channels;
	double sigma=0.0;
	double mse=0.0;
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			mse += (src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j]);
		}
	}
	mse=mse/(rowsNumber*colsNumber);
	return mse;
}

3. 图片信噪比SNR的计算

\text{SNR}(\text{dB})=10\cdot {​{\log }_{10}}\left[ \frac{\sum\limits_{x=1}^{​{​{N}_{x}}}{\sum\limits_{y=1}^{​{​{N}_{y}}}{​{​{(f(x,y))}^{2}}}}}{\sum\limits_{x=1}^{​{​{N}_{x}}}{\sum\limits_{y=1}^{​{​{N}_{y}}}{(f(x,y)-g{​{(x,y)}^{2}}}}} \right]

        其中,\(f(x,y)\)为原始图像(此处对应加噪声后的图像),\(g(x,y)\)为去噪后的图像(此处对应加噪声前的图像)。

//得到图片信噪比SNR
double getSNR(Mat & srcImage,Mat & dstImage)
{
	Mat src = dstImage;
	Mat dst = srcImage;
	int channels = dstImage.channels();
	int rowsNumber = src.rows;
	int colsNumber = src.cols*channels;
	
	double sigma=0.0;
	double mse=0.0;
	double SNR=0.0;
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			sigma += (src.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j]);
			mse += (src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j]);
		}
	}
	SNR=10*log10(sigma/mse);
	return SNR;
}

实现代码

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

using namespace cv;
using namespace std;

Mat myResize(Mat &srcImag, double width_ratio, double height_ratio);
double generateGaussianNoise(double m, double sigma);
Mat addGaussianNoise(Mat &srcImag);
double getSNR(Mat & srcImage,Mat & dstImage);
double getMSE(Mat & srcImage,Mat & dstImage);
 
int main()
{
	//调用手机IP摄像头,此处改为0则调用电脑摄像头
	VideoCapture video("http://admin:admin@xxx.xx.xxx.xxx:xxxx");
	//读取一帧图片;前几帧成像效果差,故取第10帧的图片
	Mat srcImage;
	for (int i=1; i<10; i++)
	{
		video >> srcImage;
	}
	//Mat srcImage = imread("../orange.jpg");
	//断开连接,否则手机端无法正常退出
	video.release();	
	//缩放图像
	srcImage=myResize(srcImage,0.2,0.2);
	//判断图片读取是否成功
	if (!srcImage.data)
	{
		cout << "读入图片错误!" << endl;
		return -1;
	}
	Mat dstImage = addGaussianNoise(srcImage);
	double SNR = getSNR(srcImage, dstImage);
	double MSE = getMSE(srcImage, dstImage);
	imshow("原图像", srcImage);
	imshow("添加高斯噪声后的图像", dstImage);
	cout<<"MSE: "<<MSE<<endl;
	cout<<"SNR: "<<SNR<<"dB"<<endl;
	waitKey();
	destroyAllWindows();
	return 0;
}

//调整图片大小
Mat myResize(Mat &srcImag, double width_ratio, double height_ratio)
{
	Mat src=srcImag;
	float scaleW = width_ratio;
    float scaleH = height_ratio;
    int width = int(src.cols * scaleW);
    int height = int(src.rows * scaleH);
	resize(src, src, Size(width, height));
	return src;
}

//生成高斯噪声
double generateGaussianNoise(double mu, double sigma)
{
	//定义小值
	static double z0;
	double u1, u2;
	//构造随机变量
	u1 = rand() * (1.0 / RAND_MAX);	//生成[0,1]之间的随机数
	u2 = rand() * (1.0 / RAND_MAX);	//生成[0,1]之间的随机数
	//构造高斯随机变量(Box-Muller算法)
	z0 = sqrt(-2.0*log(u1))*cos(2 * CV_PI*u2);
	return z0*sigma + mu;
}
 
//为图像添加高斯噪声
Mat addGaussianNoise(Mat &srcImag)
{
	Mat dstImage = srcImag.clone();
	int channels = dstImage.channels();
	int rowsNumber = dstImage.rows;
	int colsNumber = dstImage.cols*channels;
	//判断图像的连续性
	if (dstImage.isContinuous())
	{
		colsNumber *= rowsNumber;
		rowsNumber = 1;
	}
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			//添加高斯噪声
			int val = dstImage.ptr<uchar>(i)[j] + generateGaussianNoise(0, 1)*255;
			if (val < 0)
				val = 0;
			if (val > 255)
				val = 255;
			dstImage.ptr<uchar>(i)[j] = (uchar)val;
		}
	}
	return dstImage;
}

//得到图片信噪比SNR
double getSNR(Mat & srcImage,Mat & dstImage)
{
	Mat src = dstImage;
	Mat dst = srcImage;
	int channels = dstImage.channels();
	int rowsNumber = src.rows;
	int colsNumber = src.cols*channels;
	
	double sigma=0.0;
	double mse=0.0;
	double SNR=0.0;
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			sigma += (src.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j]);
			mse += (src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j]);
		}
	}
	SNR=10*log10(sigma/mse);
	return SNR;
}

//得到均方误差MSE
double getMSE(Mat & srcImage,Mat & dstImage)
{
	Mat src = dstImage;
	Mat dst = srcImage;
	int channels = dstImage.channels();
	int rowsNumber = src.rows;
	int colsNumber = src.cols*channels;
	double sigma=0.0;
	double mse=0.0;
	for (int i = 0; i < rowsNumber; i++)
	{
		for (int j = 0; j < colsNumber; j++)
		{
			mse += (src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j])*(src.ptr<uchar>(i)[j] - dst.ptr<uchar>(i)[j]);
		}
	}
	mse=mse/(rowsNumber*colsNumber);
	return mse;
}

CMakeLists.txt 

cmake_minimum_required(VERSION 2.8)
project(test1)
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_FLAGS "-std=c++11")
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(test1 test1.cpp)
target_link_libraries(test1 ${OpenCV_LIBS})

结果展示

(人脸必须厚码哈哈哈哈)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quentin_HIT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值