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

题目

  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(i)[j] - dst.ptr(i)[j])*(src.ptr(i)[j] - dst.ptr(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(i)[j])*(src.ptr(i)[j]);
			mse += (src.ptr(i)[j] - dst.ptr(i)[j])*(src.ptr(i)[j] - dst.ptr(i)[j]);
		}
	}
	SNR=10*log10(sigma/mse);
	return SNR;
}

实现代码

#include 
#include 

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:[email protected]: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: "<(i)[j] + generateGaussianNoise(0, 1)*255;
			if (val < 0)
				val = 0;
			if (val > 255)
				val = 255;
			dstImage.ptr(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(i)[j])*(src.ptr(i)[j]);
			mse += (src.ptr(i)[j] - dst.ptr(i)[j])*(src.ptr(i)[j] - dst.ptr(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(i)[j] - dst.ptr(i)[j])*(src.ptr(i)[j] - dst.ptr(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})

结果展示

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

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

你可能感兴趣的:(OpenCV,C++,opencv,c++)