最近在做圖像算法,對圖像性能指標進行測試。主要包括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是指經過簡單的數字指紋加密的圖像或失真的圖像。(這裡用到的數字指紋加密是指將圖像隨機數量像素點進行小范圍的改變,肉眼無法辨別)。
1 #include <cstdio>
2 #include <cmath>
3 #include <ctime>
4 #include <cstdlib>
5
6 #include "cv.h"
7 #include "highgui.h"
8
9
10 //計算兩幅圖像的保真性
11 //參數: src 原圖像
12 // key 指紋加密的圖像
13
14 double Psnr( IplImage* src, IplImage* key ) {
15 int width = src->width; //圖像寬
16 int height = src->height; //圖像高
17
18 double mse = 0.0; //MSE為均方差
19
20 CvScalar s_src; //原圖像的像素通道結構體
21 CvScalar s_key; //加密後的像素通道結構體
22
23 //計算MSE——均方差
24 for( int row = 0; row < height; row++ ) {
25 for( int col = 0; col < width; col++ ) {
26 s_src = cvGet2D( src, row, col );
27 s_key = cvGet2D( key, row, col );
28 double src_r = s_src.val[0]; //取出G通道的像素值
29 double key_r = s_key.val[0];
30 //if( src_r != key_r ) {
31 //printf( "%.lf %.lf\n", src_r, key_r );
32 //char ch = getchar();
33 //}
34 mse += ( src_r - key_r ) * ( src_r - key_r ); //計算方差
35 }
36 }
37
38 const double MAX = 255.0; //最大峰值為255
39
40 //方法一
41 double temp = MAX * MAX * ( double )width * ( double )height;
42 double r = temp / mse;
43 r = 10.0 * log10( r );
44
45 //方法二,計算結果是一樣的
46 //mse = mse / ( width * height );
47 //printf( "均方誤差: %lf\n", mse );
48 //mse = sqrt( mse );
49 //double temp = MAX;
50 //double r = temp / mse;
51 //r = 20.0 * log10( r );
52
53 //打印的中間結果信息
54 //printf( "temp: %lf\n", temp );
55 //printf( "sum: %lf\n", sum );
56 //printf( "%lf\n", r );
57
58 return r;
59 }
60
61
62
63 //計算兩幅圖像的魯棒性
64 //參數: src 原圖像
65 // key 指紋加密的圖像
66
67 double Nc( IplImage* src, IplImage* key ) {
68 int width = src->width; //圖像寬
69 int height = src->height; //圖像高
70
71 CvScalar s_src; //原圖像的像素通道結構體
72 CvScalar s_key; //加密後的像素通道結構體
73
74 double d = 0.0;
75 double d_src = 0.0;
76 double d_key = 0.0;
77
78 for( int row = 0; row < height; row++ ) {
79 for( int col = 0; col < width; col++ ) {
80 s_src = cvGet2D( src, row, col );
81 s_key = cvGet2D( key, row, col );
82 double src_r = s_src.val[0]; //取出G通道的像素值
83 double key_r = s_key.val[0];
84 d += src_r * key_r;
85 d_src += src_r * src_r;
86 d_key += key_r * key_r;
87 }
88 }
89
90 //nc是魯棒性指標
91 double nc = 0.0;
92 nc = d / ( sqrt( d_src ) * sqrt( d_key ) );
93
94 return nc;
95 }
96
97
98
99 //計算兩幅圖像的結構相似性
100 //參數: src 原圖像
101 // key 指紋加密的圖像
102
103 double Ssim( IplImage* src, IplImage* key ) {
104 int width = src->width; //圖像寬
105 int height = src->height; //圖像高
106
107 CvScalar s_src; //原圖像的像素通道結構體
108 CvScalar s_key; //加密後的像素通道結構體
109
110 double mu_src = 0.0; //原圖像均值
111 double mu_key = 0.0; //加密後的圖像均值
112
113 int row, col;
114 for( row = 0; row < height; row++ ) {
115 for( col = 0; col < width; col++ ) {
116 s_src = cvGet2D( src, row, col );
117 s_key = cvGet2D( key, row, col );
118 double src_r = s_src.val[0]; //取出G通道的像素值
119 double key_r = s_key.val[0];
120 mu_src += src_r;
121 mu_key += key_r;
122 }
123 }
124
125 mu_src = mu_src / ( width * height ); //原圖像均值
126 mu_key = mu_key / ( width * height ); //加密圖像均值
127
128 //打印的中間結果信息
129 //printf( "src的均值: %lf\n", mu_src );
130 //printf( "key的均值: %lf\n", mu_key );
131
132 double sigma_src2 = 0.0; //原圖像方差,即sigma_src^2
133 double sigma_key2 = 0.0; //加密圖像方差,即sigma_key^2
134 double sigma_s_k2 = 0.0; //原圖和加密圖像的方差,即sigma_s_k^2
135
136 double sigma_src = 0.0; //原圖像標准差
137 double sigma_key = 0.0; //加密圖像標准差
138 double sigma_s_k = 0.0; //原圖像和加密圖像的標准差
139
140 for( row = 0; row < height; row++ ) {
141 for( col = 0; col < width; col++ ) {
142 s_src = cvGet2D( src, row, col );
143 s_key = cvGet2D( key, row, col );
144 double src_r = s_src.val[0]; //取出G通道的像素值
145 double key_r = s_key.val[0];
146 sigma_src2 += ( src_r - mu_src ) * ( src_r - mu_src );
147 sigma_key2 += ( key_r - mu_key ) * ( key_r - mu_key );
148 sigma_s_k2 += ( src_r - mu_src ) * ( key_r - mu_key );
149 }
150 }
151
152 sigma_src2 = sigma_src2 / ( width * height - 1 );
153 sigma_key2 = sigma_key2 / ( width * height - 1 );
154 sigma_s_k2 = sigma_s_k2 / ( width * height - 1 );
155
156 sigma_src = sqrt( sigma_src2 );
157 sigma_key = sqrt( sigma_key2 );
158 sigma_s_k = sqrt( sigma_s_k2 );
159
160 //打印的中間結果信息
161 //printf( "sigma_src: %lf\n", sigma_src );
162 //printf( "sigma_key: %lf\n", sigma_key );
163 //printf( "sigma_s_k: %lf\n", sigma_s_k );
164
165 //固定參數,為常量
166 //c1,c2,c3是用來維持穩定的常數
167 //MAX是像素值的動態范圍
168 const double k1 = 0.01;
169 const double k2 = 0.03;
170 const int MAX = 255;
171
172 double c1 = ( k1 * MAX ) * ( k1 * MAX );
173 double c2 = ( k2 * MAX ) * ( k2 * MAX );
174 double c3 = c2 / 2;
175
176 //亮度、對比度、結構三方面度量圖像相似性
177 double light = ( 2 * mu_src * mu_key + c1 ) / ( mu_src * mu_src + mu_key * mu_key + c1 );
178 double contrast = ( 2 * sigma_src * sigma_key + c2 ) / ( sigma_src2 + sigma_key2 +c2 );
179 double structure = ( sigma_s_k2 + c3 ) / ( sigma_src * sigma_key + c3 );
180
181 //打印的中間結果信息
182 //printf( "light: %lf\n", light );
183 //printf( "contrast: %lf\n", contrast );
184 //printf( "structure: %lf\n", structure );
185
186 //方法一
187 //亮度 * 對比度 * 結構相似度
188 double ssim = light * contrast * structure;
189
190 //方法二,計算結果是一樣的
191 //double ssim = light * ( ( 2 * sigma_s_k2 + c2 ) / (sigma_src2 + sigma_key2 + c2 ) );
192
193 return ssim;
194 }
195
196 void KeyImg( IplImage* src ) {
197 int n = 0;
198 int width = src->width; //圖像寬
199 int height = src->height; //圖像高
200
201 printf( "圖像寬: %d, 高: %d\n", width, height );
202 printf( "輸入嵌入的像素點位數: " );
203 scanf( "%d", &n );
204 int count = 0;
205
206 srand( (unsigned)time(NULL) );
207
208 CvScalar s;
209
210 IplImage* keyImg;
211 keyImg = cvCreateImage( cvGetSize( src ), IPL_DEPTH_8U, 1 );
212 keyImg = cvCloneImage( src );
213
214 while( count < n ) {
215 int x = rand() % width;
216 int y = rand() % height;
217 s = cvGet2D( keyImg, y, x );
218 double b = s.val[0];
219 double g = s.val[1];
220 double r = s.val[2];
221 //printf( "修改前: %0.lf, %0.lf, %0.lf\n ", s.val[0], s.val[1], s.val[2] );
222 int temp = rand() % 2;
223 if( temp == 0 ) {
224 s.val[0] -= 10.0;
225 }
226 else if( temp == 1 ) {
227 s.val[0] += 10.0;
228 }
229 cvSet2D( keyImg, y, x, s );
230 //s = cvGet2D( keyImg, y, x );
231 //printf( "修改後: %0.lf\n", s.val[0] );
232 count++;
233 }
234
235 cvSaveImage( "Test.bmp", keyImg );
236 //cvNamedWindow("image", CV_WINDOW_AUTOSIZE); //創建窗口
237 //cvShowImage("image", keyImg); //顯示圖像
238 cvWaitKey(0);
239 printf( "圖像生成成功!\n" );
240 }
241
242
243 int main() {
244 IplImage* src = NULL;
245 IplImage* key = NULL;
246
247
248 src = cvLoadImage( "Flower.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );
249
250 //生成一張加密的指紋圖片
251 //KeyImg( src );
252 key = cvLoadImage( "水印Flower.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );
253
254 double psnr = 0.0;
255 double nc = 0.0;
256 double ssim = 0.0;
257 psnr = Psnr( src, key );
258 nc = Nc( src, key );
259 ssim = Ssim( src, key );
260 printf( "保真性: %lf\n", psnr );
261 printf( "魯棒性: %lf\n", nc );
262 printf( "結構相似性: %lf\n", ssim );
263 return 0;
264 }
測試一:使用Windows自帶的“郁金香”圖片,先生成數字指紋圖像,比較圖像的失真結果。

原圖 數字指紋加密後
測試結果:

測試二:用原圖和經過噪聲攻擊的圖像進行測試。

測試結果:
