您的位置:首页 > 运维架构

OpenCV对图像的性能测试

2016-07-22 15:07 585 查看
最近在做图像算法,对图像性能指标进行测试。主要包括PSNR(峰值信噪比)、NC(归一化相关系数)、SSIM(结构相似性)等,下面对这三个指标做简单介绍。

PSNR:峰值信噪比,一种评价图像的客观标准,用来评估图像的保真性。峰值信噪比经常用作图像压缩等领域中信号重建质量的测量方法,它常简单地通过均方差(MSE)进行定义,使用两个m×n单色图像I和K。PSNR的单位为分贝dB。计算公式如下:





其中,MAXI是表示图像点颜色的最大数值,如果每个采样点用 8 位表示,那么就是 255。PSNR值越大,就代表失真越少,图像压缩中典型的峰值信噪比值在 30 到 40dB 之间,小于30dB时考虑图像无法忍受。

NC:对两幅图像进行相似度的衡量,除了用眼睛观察的方法外,我们可以更加精确地用数据来客观的评估归一化,归一化的相关系数(NC)提供了度量工具,它可以用来评估图像的鲁棒性



其中,w(x,y)和w'(x,y)代表两张图像(水印和提取水印),M、N为图像分标率,归一化相关系数用来表示原始水印与提取水印的相似度,取值在0到1之间,越接近1表示鲁棒性越好。

SSIM:(structural similarity index),结构相似性,是一种衡量两幅图像相似度的指标,也是一种全参考的图像质量评价指标,它分别从亮度对比度结构三方面度量图像相似性。Wiki中的计算公式:



其中



的平均值,



的平均值,



的方差,



的方差,





的标准差。



是用来维持稳定的常数。

是像素值的动态范围。



。结构相似性的范围为



。当两张图像一模一样时,SSIM的值等于1。

另一篇博文中的计算公式如下,也可以使用。http://blog.csdn.net/xiaxiazls/article/details/47952611



其中ux、uy分别表示图像X和Y的均值,σX、σY分别表示图像X和Y的方差,σXY表示图像X和Y的协方差,即



C1、C2、C3为常数,为了避免分母为0的情况,通常取C1=(K1*L)^2, C2=(K2*L)^2, C3=C2/2, 一般地K1=0.01, K2=0.03, L=255. 则



在实际应用中,可以利用滑动窗将图像分块,令分块总数为N,考虑到窗口形状对分块的影响,采用高斯加权计算每一窗口的均值、方差以及协方差,然后计算对应块的结构相似度SSIM,最后将平均值作为两图像的结构相似性度量,即平均结构相似性MSSIM:



另外可以参考SSIM的另一篇博文: http://blog.csdn.net/xiaxiazls/article/details/47952611
程序如下:

环境:win7 VC++6.0 OpenCV1.0

其中 src 是原图像,key是指经过简单的数字指纹加密的图像或失真的图像。(这里用到的数字指纹加密是指将图像随机数量像素点进行小范围的改变,肉眼无法辨别)。

#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstdlib>

#include "cv.h"
#include "highgui.h"

//计算两幅图像的保真性
//参数: src 原图像
//      key 指纹加密的图像

double Psnr( IplImage* src, IplImage* key ) {
int width = src->width;       //图像宽
int height = src->height;     //图像高

double mse = 0.0;             //MSE为均方差

CvScalar s_src;               //原图像的像素通道结构体
CvScalar s_key;               //加密后的像素通道结构体

//计算MSE——均方差
for( int row = 0; row < height; row++ ) {
for( int col = 0; col < width; col++ ) {
s_src = cvGet2D( src, row, col );
s_key = cvGet2D( key, row, col );
double src_r = s_src.val[0];        //取出G通道的像素值
double key_r = s_key.val[0];
//if( src_r != key_r ) {
//printf( "%.lf %.lf\n", src_r, key_r );
//char ch = getchar();
//}
mse += ( src_r - key_r ) * ( src_r - key_r );   //计算方差
}
}

const double MAX = 255.0;               //最大峰值为255

//方法一
double temp = MAX * MAX * ( double )width * ( double )height;
double r = temp / mse;
r = 10.0 * log10( r );

//方法二,计算结果是一样的
//mse = mse / ( width * height );
//printf( "均方误差: %lf\n", mse );
//mse = sqrt( mse );
//double temp = MAX;
//double r = temp / mse;
//r = 20.0 * log10( r );

//打印的中间结果信息
//printf( "temp: %lf\n", temp );
//printf( "sum: %lf\n", sum );
//printf( "%lf\n", r );

return r;
}

//计算两幅图像的鲁棒性
//参数: src 原图像
//      key 指纹加密的图像

double Nc( IplImage* src, IplImage* key ) {
int width = src->width;       //图像宽
int height = src->height;     //图像高

CvScalar s_src;               //原图像的像素通道结构体
CvScalar s_key;               //加密后的像素通道结构体

double d = 0.0;
double d_src = 0.0;
double d_key = 0.0;

for( int row = 0; row < height; row++ ) {
for( int col = 0; col < width; col++ ) {
s_src = cvGet2D( src, row, col );
s_key = cvGet2D( key, row, col );
double src_r = s_src.val[0];        //取出G通道的像素值
double key_r = s_key.val[0];
d += src_r * key_r;
d_src += src_r * src_r;
d_key += key_r * key_r;
}
}

//nc是鲁棒性指标
double nc = 0.0;
nc = d / ( sqrt( d_src ) * sqrt( d_key ) );

return nc;
}

//计算两幅图像的结构相似性
//参数: src 原图像
//      key 指纹加密的图像

double Ssim( IplImage* src, IplImage* key ) {
int width = src->width;       //图像宽
int height = src->height;     //图像高

CvScalar s_src;               //原图像的像素通道结构体
CvScalar s_key;               //加密后的像素通道结构体

double mu_src = 0.0;          //原图像均值
double mu_key = 0.0;          //加密后的图像均值

int row, col;
for( row = 0; row < height; row++ ) {
for( col = 0; col < width; col++ ) {
s_src = cvGet2D( src, row, col );
s_key = cvGet2D( key, row, col );
double src_r = s_src.val[0];        //取出G通道的像素值
double key_r = s_key.val[0];
mu_src += src_r;
mu_key += key_r;
}
}

mu_src = mu_src / ( width * height );       //原图像均值
mu_key = mu_key / ( width * height );       //加密图像均值

//打印的中间结果信息
//printf( "src的均值: %lf\n", mu_src );
//printf( "key的均值: %lf\n", mu_key );

double sigma_src2 = 0.0;                    //原图像方差,即sigma_src^2
double sigma_key2 = 0.0;                    //加密图像方差,即sigma_key^2
double sigma_s_k2 = 0.0;                    //原图和加密图像的方差,即sigma_s_k^2

double sigma_src = 0.0;                     //原图像标准差
double sigma_key = 0.0;                     //加密图像标准差
double sigma_s_k = 0.0;                     //原图像和加密图像的标准差

for( row = 0; row < height; row++ ) {
for( col = 0; col < width; col++ ) {
s_src = cvGet2D( src, row, col );
s_key = cvGet2D( key, row, col );
double src_r = s_src.val[0];        //取出G通道的像素值
double key_r = s_key.val[0];
sigma_src2 += ( src_r - mu_src ) * ( src_r - mu_src );
sigma_key2 += ( key_r - mu_key ) * ( key_r - mu_key );
sigma_s_k2 += ( src_r - mu_src ) * ( key_r - mu_key );
}
}

sigma_src2 = sigma_src2 / ( width * height - 1 );
sigma_key2 = sigma_key2 / ( width * height - 1 );
sigma_s_k2 = sigma_s_k2 / ( width * height - 1 );

sigma_src = sqrt( sigma_src2 );
sigma_key = sqrt( sigma_key2 );
sigma_s_k = sqrt( sigma_s_k2 );

//打印的中间结果信息
//printf( "sigma_src: %lf\n", sigma_src );
//printf( "sigma_key: %lf\n", sigma_key );
//printf( "sigma_s_k: %lf\n", sigma_s_k );

//固定参数,为常量
//c1,c2,c3是用来维持稳定的常数
//MAX是像素值的动态范围
const double k1 = 0.01;
const double k2 = 0.03;
const int MAX = 255;

double c1 = ( k1 * MAX ) * ( k1 * MAX );
double c2 = ( k2 * MAX ) * ( k2 * MAX );
double c3 = c2 / 2;

//亮度、对比度、结构三方面度量图像相似性
double light = ( 2 * mu_src * mu_key + c1 ) / ( mu_src * mu_src + mu_key * mu_key + c1 );
double contrast = ( 2 * sigma_src * sigma_key + c2 ) / ( sigma_src2 + sigma_key2 +c2 );
double structure = ( sigma_s_k2 + c3 ) / ( sigma_src * sigma_key + c3 );

//打印的中间结果信息
//printf( "light: %lf\n", light );
//printf( "contrast: %lf\n", contrast );
//printf( "structure: %lf\n", structure );

//方法一
//亮度 * 对比度 * 结构相似度
double ssim = light * contrast * structure;

//方法二,计算结果是一样的
//double ssim = light * ( ( 2 * sigma_s_k2 + c2 ) / (sigma_src2 + sigma_key2 + c2 ) );

return ssim;
}

void KeyImg( IplImage* src ) {
int n = 0;
int width = src->width;       //图像宽
int height = src->height;     //图像高

printf( "图像宽: %d, 高: %d\n", width, height );
printf( "输入嵌入的像素点位数: " );
scanf( "%d", &n );
int count = 0;

srand( (unsigned)time(NULL) );

CvScalar s;

IplImage* keyImg;
keyImg = cvCreateImage( cvGetSize( src ), IPL_DEPTH_8U, 1 );
keyImg = cvCloneImage( src );

while( count < n ) {
int x = rand() % width;
int y = rand() % height;
s = cvGet2D( keyImg, y, x );
double b = s.val[0];
double g = s.val[1];
double r = s.val[2];
//printf( "修改前: %0.lf, %0.lf, %0.lf\n  ", s.val[0], s.val[1], s.val[2] );
int temp = rand() % 2;
if( temp == 0 ) {
s.val[0] -= 10.0;
}
else if( temp == 1 ) {
s.val[0] += 10.0;
}
cvSet2D( keyImg, y, x, s );
//s = cvGet2D( keyImg, y, x );
//printf( "修改后: %0.lf\n", s.val[0] );
count++;
}

cvSaveImage( "Test.bmp", keyImg );
//cvNamedWindow("image", CV_WINDOW_AUTOSIZE); //创建窗口
//cvShowImage("image", keyImg); //显示图像
cvWaitKey(0);
printf( "图像生成成功!\n" );
}

int main() {
IplImage* src = NULL;
IplImage* key = NULL;

src = cvLoadImage( "Flower.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );

//生成一张加密的指纹图片
//KeyImg( src );
key = cvLoadImage( "水印Flower.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );

double psnr = 0.0;
double nc = 0.0;
double ssim = 0.0;
psnr = Psnr( src, key );
nc = Nc( src, key );
ssim = Ssim( src, key );
printf( "保真性: %lf\n", psnr );
printf( "鲁棒性: %lf\n", nc );
printf( "结构相似性: %lf\n", ssim );
return 0;
}


测试一:使用Windows自带的“郁金香”图片,先生成数字指纹图像,比较图像的失真结果。





原图 数字指纹加密后

测试结果:



测试二:用原图和经过噪声攻击的图像进行测试。



测试结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: