在一些应用场景中,一些RGB图片直接转为灰度图片的效果很不好,原本不同的颜色很可能在转为灰度后区分度太小,而导致丢失了对比度信息。例如下面这副图片
直接转为灰度的结果:
可以发现,基本上无法区分这两种颜色了。
1 Mat W = (Mat_<double>(66, 3) << 0, 0, 1.0000,
2 0, 0.1000, 0.9000,
3 0, 0.2000, 0.8000,
4 0, 0.3000, 0.7000,
5 0, 0.4000, 0.6000,
6 0, 0.5000, 0.5000,
7 0, 0.6000, 0.4000,
8 0, 0.7000, 0.3000,
9 0, 0.8000, 0.2000,
10 0, 0.9000, 0.1000,
11 0, 1.0000, 0,
12 0.1000, 0, 0.9000,
13 0.1000, 0.1000, 0.8000,
14 0.1000, 0.2000, 0.7000,
15 0.1000, 0.3000, 0.6000,
16 0.1000, 0.4000, 0.5000,
17 0.1000, 0.5000, 0.4000,
18 0.1000, 0.6000, 0.3000,
19 0.1000, 0.7000, 0.2000,
20 0.1000, 0.8000, 0.1000,
21 0.1000, 0.9000, 0,
22 0.2000, 0, 0.8000,
23 0.2000, 0.1000, 0.7000,
24 0.2000, 0.2000, 0.6000,
25 0.2000, 0.3000, 0.5000,
26 0.2000, 0.4000, 0.4000,
27 0.2000, 0.5000, 0.3000,
28 0.2000, 0.6000, 0.2000,
29 0.2000, 0.7000, 0.1000,
30 0.2000, 0.8000, 0,
31 0.3000, 0, 0.7000,
32 0.3000, 0.1000, 0.6000,
33 0.3000, 0.2000, 0.5000,
34 0.3000, 0.3000, 0.4000,
35 0.3000, 0.4000, 0.3000,
36 0.3000, 0.5000, 0.2000,
37 0.3000, 0.6000, 0.1000,
38 0.3000, 0.7000, 0.0000,
39 0.4000, 0, 0.6000,
40 0.4000, 0.1000, 0.5000,
41 0.4000, 0.2000, 0.4000,
42 0.4000, 0.3000, 0.3000,
43 0.4000, 0.4000, 0.2000,
44 0.4000, 0.5000, 0.1000,
45 0.4000, 0.6000, 0.0000,
46 0.5000, 0, 0.5000,
47 0.5000, 0.1000, 0.4000,
48 0.5000, 0.2000, 0.3000,
49 0.5000, 0.3000, 0.2000,
50 0.5000, 0.4000, 0.1000,
51 0.5000, 0.5000, 0,
52 0.6000, 0, 0.4000,
53 0.6000, 0.1000, 0.3000,
54 0.6000, 0.2000, 0.2000,
55 0.6000, 0.3000, 0.1000,
56 0.6000, 0.4000, 0.0000,
57 0.7000, 0, 0.3000,
58 0.7000, 0.1000, 0.2000,
59 0.7000, 0.2000, 0.1000,
60 0.7000, 0.3000, 0.0000,
61 0.8000, 0, 0.2000,
62 0.8000, 0.1000, 0.1000,
63 0.8000, 0.2000, 0.0000,
64 0.9000, 0, 0.1000,
65 0.9000, 0.1000, 0.0000,
66 1.0000, 0, 0);
然后,由于一般较大原图像颜色信息是冗余的,因此可以将图像缩小以加快速度,作者推荐将图像缩小到 64*64 的大小,我这里将图像按比例缩小到了小边为 64 的大小。
此外,论文作者还提出只选取 64*64 个随机点对 (相当与每个小图的点对应一个随机的点) 来计算,再次提高速度。
代码实现
1 Mat RTCP_BGR2Gary3(const Mat &_image)
2 {
3 int height = _image.rows;
4 int width = _image.cols;
5 const double sigma = 0.05;
6 const int smallScale = 64;
7
8 Mat W = (Mat_<double>(66, 3) << 0, 0, 1.0000,
9 0, 0.1000, 0.9000,
10 0, 0.2000, 0.8000,
11 0, 0.3000, 0.7000,
12 0, 0.4000, 0.6000,
13 0, 0.5000, 0.5000,
14 0, 0.6000, 0.4000,
15 0, 0.7000, 0.3000,
16 0, 0.8000, 0.2000,
17 0, 0.9000, 0.1000,
18 0, 1.0000, 0,
19 0.1000, 0, 0.9000,
20 0.1000, 0.1000, 0.8000,
21 0.1000, 0.2000, 0.7000,
22 0.1000, 0.3000, 0.6000,
23 0.1000, 0.4000, 0.5000,
24 0.1000, 0.5000, 0.4000,
25 0.1000, 0.6000, 0.3000,
26 0.1000, 0.7000, 0.2000,
27 0.1000, 0.8000, 0.1000,
28 0.1000, 0.9000, 0,
29 0.2000, 0, 0.8000,
30 0.2000, 0.1000, 0.7000,
31 0.2000, 0.2000, 0.6000,
32 0.2000, 0.3000, 0.5000,
33 0.2000, 0.4000, 0.4000,
34 0.2000, 0.5000, 0.3000,
35 0.2000, 0.6000, 0.2000,
36 0.2000, 0.7000, 0.1000,
37 0.2000, 0.8000, 0,
38 0.3000, 0, 0.7000,
39 0.3000, 0.1000, 0.6000,
40 0.3000, 0.2000, 0.5000,
41 0.3000, 0.3000, 0.4000,
42 0.3000, 0.4000, 0.3000,
43 0.3000, 0.5000, 0.2000,
44 0.3000, 0.6000, 0.1000,
45 0.3000, 0.7000, 0.0000,
46 0.4000, 0, 0.6000,
47 0.4000, 0.1000, 0.5000,
48 0.4000, 0.2000, 0.4000,
49 0.4000, 0.3000, 0.3000,
50 0.4000, 0.4000, 0.2000,
51 0.4000, 0.5000, 0.1000,
52 0.4000, 0.6000, 0.0000,
53 0.5000, 0, 0.5000,
54 0.5000, 0.1000, 0.4000,
55 0.5000, 0.2000, 0.3000,
56 0.5000, 0.3000, 0.2000,
57 0.5000, 0.4000, 0.1000,
58 0.5000, 0.5000, 0,
59 0.6000, 0, 0.4000,
60 0.6000, 0.1000, 0.3000,
61 0.6000, 0.2000, 0.2000,
62 0.6000, 0.3000, 0.1000,
63 0.6000, 0.4000, 0.0000,
64 0.7000, 0, 0.3000,
65 0.7000, 0.1000, 0.2000,
66 0.7000, 0.2000, 0.1000,
67 0.7000, 0.3000, 0.0000,
68 0.8000, 0, 0.2000,
69 0.8000, 0.1000, 0.1000,
70 0.8000, 0.2000, 0.0000,
71 0.9000, 0, 0.1000,
72 0.9000, 0.1000, 0.0000,
73 1.0000, 0, 0);
74 //缩小图片
75 Mat smallImg;
76 if (height > smallScale && width > smallScale)
77 {
78 Size small_size;
79 if (height > width)
80 {
81 double scale = double(smallScale) / double(height);
82 small_size = Size(int(width*scale), smallScale);
83 }
84 else
85 {
86 double scale = double(smallScale) / double(width);
87 small_size = Size(smallScale, int(height*scale));
88 }
89 resize(_image, smallImg, small_size, 0, 0, INTER_NEAREST);
90 }
91 else
92 {
93 smallImg = _image;
94 }
95 //将smallImg与拉平
96 Mat sImgArray = smallImg.reshape(0, 1);
97 //生成一个随机打乱的sImgArrayShuffle;
98 Mat sImgArrayShuffle = sImgArray.clone();
99 time_t t = time(NULL);
100 RNG rng(t);
101 randShuffle(sImgArrayShuffle,1.0,&rng);
102 //Lab空间转换
103 Mat sImgArrayLab, sImgArrayShuffleLab;
104 cvtColor(sImgArray, sImgArrayLab, CV_BGR2Lab);
105 cvtColor(sImgArrayShuffle, sImgArrayShuffleLab, CV_BGR2Lab);
106 //类型转换
107 sImgArray.convertTo(sImgArray, CV_64FC3, 1 / 255.0);
108 sImgArrayShuffle.convertTo(sImgArrayShuffle, CV_64FC3, 1 / 255.0);
109 sImgArrayLab.convertTo(sImgArrayLab, CV_64FC3, 1 / 255.0);
110 sImgArrayShuffleLab.convertTo(sImgArrayShuffleLab, CV_64FC3, 1 / 255.0);
111 //计算各个W参数的能量E(这里最好用矩阵计算,我直接循环了)
112 int array_size = sImgArray.cols;
113 vector<double> E(W.rows,0);
114 int E_size = E.size();
115 cout << E_size << endl;
116 for (int i = 0; i < array_size; i++)
117 {
118
119 double delta = sqrt(pow(sImgArrayLab.at<Vec3d>(0, i)[0] - sImgArrayShuffleLab.at<Vec3d>(0, i)[0], 2)
120 + pow(sImgArrayLab.at<Vec3d>(0, i)[1] - sImgArrayShuffleLab.at<Vec3d>(0, i)[1], 2)
121 + pow(sImgArrayLab.at<Vec3d>(0, i)[2] - sImgArrayShuffleLab.at<Vec3d>(0, i)[2], 2));
122 if (delta < 0.05)
123 continue;
124 for (int n = 0; n < E_size; n++)
125 {
126 double delta_g = (sImgArray.at<Vec3d>(0, i)[0] - sImgArrayShuffle.at<Vec3d>(0, i)[0])*W.at<double>(n, 0)
127 + (sImgArray.at<Vec3d>(0, i)[1] - sImgArrayShuffle.at<Vec3d>(0, i)[1])*W.at<double>(n, 1)
128 + (sImgArray.at<Vec3d>(0, i)[2] - sImgArrayShuffle.at<Vec3d>(0, i)[2])*W.at<double>(n, 2);
129 double tmp_E = log(exp(-pow(delta_g + delta, 2) / (2 * pow(sigma, 2)))+ exp(-pow(delta_g - delta, 2) / (2 * pow(sigma, 2))));
130 E[n] += tmp_E;
131 }
132 }
133 //找出最大E的下标
134 double MAX_E = E[0];
135 int MAX_i = 0;
136 for (int i = 0; i < E_size; i++)
137 {
138 if (MAX_E < E[i])
139 {
140 MAX_E = E[i];
141 MAX_i = i;
142 }
143 }
144 cout << MAX_i << endl;
145 //转灰度
146 Mat gray(_image.size(), CV_8UC1);
147 for (int i = 0; i < width; i++)
148 {
149 for (int j = 0; j < height; j++)
150 {
151 gray.at<uchar>(j, i) = W.at<double>(MAX_i, 0)*_image.at<Vec3b>(j, i)[0]
152 + W.at<double>(MAX_i, 1)*_image.at<Vec3b>(j, i)[1]
153 + W.at<double>(MAX_i, 2)*_image.at<Vec3b>(j, i)[2];
154 }
155 }
156 return gray;
157 }
结果
与论文结果有点不一样。。不过确实有效果
原图
Opencv 直接转灰度
用上面那个代码转灰度的结果