色阶调整( Levles Adjustment )
(一)色阶调整原理
色阶是什么:色阶就是用直方图描述出的整张图片的明暗信息。如图
从左至右是从暗到亮的像素分布,黑色三角代表最暗地方(纯黑),白色三角代表最亮地方(纯白)。灰色三角代表中间调。
每一个色阶定义有两组值:
一组是输入色阶值,包含黑灰白三个值, 上图中: 黑点值为0, 灰点为1.00,白点为255
另一组是输入色阶值,包含黑白两个值,上图中:输出色阶黑为0,白为255
对于一个RGB图像, 可以对R, G, B 通道进行独立的色阶调整,即,对三个通道分别使用三个色阶定义值。还可以再对 三个通道进行整体色阶调整。 因此,对一个图像,可以用四次色阶调整。最终的结果,是四次调整后合并产生的结果。
我们先来分析对单通道的色阶原理,比如:对红色通道定义色阶调整如下:
则此时: 输入色阶值为: 黑13, 灰1.29, 白240, 输出色阶值为:黑11,白242
则色阶调整的实现是: 当输入值<黑点值(13)时,全部变为输出色阶的黑值。 当输入值>白点(240)时,全部变为输出色阶的白值
当输入值介于黑值与白值之间(13-240)时,则结合灰度系数,按比例重新计算,变为一个新的值。
对红、绿、蓝三个独立通道调整方式都与上述算法相同。各通道调整是互不相关的。
对RGB通道进行整体调整时,则对RGB三个值进行同时变换。
(二)色阶调整的OpenCV实现
我用opencv写了两个 C++ 类: Levels类实现了多通道的色阶的定义、实施调整。 Level类是一个通道的色阶定义类。
源码共两个文件: Levels.hpp, Levels.cpp
源码有一定的长度,不具体解释了,请见注释。
补充说明几点:
1, Levels类中定义了四个Level对象(即四个通道),分别是RedChannel, GreenChannel, BlueChannel 和 RGBChannel.
2,每个Level对象有五个属性值:
1 int Shadow; //输入色阶黑点值 2 float Midtones; //输入色阶灰点值(注意是浮点数) 3 int Highlight; //输入色阶白点值 4 int OutputShadow; //输出色阶黑点值 5 int OutputHighlight; //输出色阶白点值
3, 使用方法:创建一个Levels对象,然后对其所属的Level对象的属性值进行赋值,然后调整 Levels类的adjust()方法,即可实现色阶调整。
(三)例程
写一个例程,使用Levels类,实现色阶调整。
程序中定义了两个窗口,一个是图片窗口,一个是色阶定义窗口。
1 #include <cstdio>
2 #include <iostream>
3 #include "opencv2/core.hpp"
4 #include "opencv2/imgproc.hpp"
5 #include "opencv2/highgui.hpp"
6
7 #include "Levels.hpp"
8
9 using namespace std;
10 using namespace cv;
11
12 static string window_name = "Photo";
13 static Mat src;
14
15 static Mat levels_mat;
16 static string levels_window = "Adjust Levels";
17 static int channel = 0;
18 Levels levels;
19
20 int Shadow;
21 int Midtones = 100;
22 int Highlight;
23 int OutputShadow;
24 int OutputHighlight;
25
26 static void invalidate()
27 {
28 Mat dst;
29 levels.adjust(src, dst);
30 imshow(window_name, dst);
31
32 imshow(levels_window, levels_mat);
33 }
34
35 static void channelRead(int which_channel)
36 {
37 channel = which_channel;
38 Level * CurrentChannel = NULL;
39 switch (channel) {
40 case 0: CurrentChannel = &levels.RGBChannel; break;
41 case 1: CurrentChannel = &levels.RedChannel; break;
42 case 2: CurrentChannel = &levels.GreenChannel; break;
43 case 3: CurrentChannel = &levels.BlueChannel; break;
44 }
45 if ( CurrentChannel == NULL ) return;
46
47 Shadow = CurrentChannel->Shadow;
48 Midtones = int (CurrentChannel->Midtones * 100);
49 Highlight = CurrentChannel->Highlight;
50 OutputShadow = CurrentChannel->OutputShadow;
51 OutputHighlight = CurrentChannel->OutputHighlight;
52
53 }
54
55 static void channelWrite()
56 {
57 Level * CurrentChannel = NULL;
58 switch (channel) {
59 case 0: CurrentChannel = &levels.RGBChannel; break;
60 case 1: CurrentChannel = &levels.RedChannel; break;
61 case 2: CurrentChannel = &levels.GreenChannel; break;
62 case 3: CurrentChannel = &levels.BlueChannel; break;
63 }
64
65 if ( CurrentChannel == NULL )
66 return ;
67
68 CurrentChannel->Shadow = Shadow;
69 CurrentChannel->Midtones = Midtones / 100.0;
70 CurrentChannel->Highlight = Highlight;
71 CurrentChannel->OutputShadow = OutputShadow;
72 CurrentChannel->OutputHighlight = OutputHighlight;
73
74 invalidate();
75 }
76
77
78 static void callbackAdjust(int , void *)
79 {
80 channelWrite();
81 invalidate();
82 }
83
84
85 static void callbackAdjustChannel(int , void *)
86 {
87 channelRead(channel);
88 setTrackbarPos("Shadow", levels_window, Shadow);
89 setTrackbarPos("Midtones", levels_window, Midtones);
90 setTrackbarPos("Highlight", levels_window, Highlight);
91 setTrackbarPos("OutShadow", levels_window, OutputShadow);
92 setTrackbarPos("OutHighlight", levels_window, OutputHighlight);
93 invalidate();
94 }
95
96
97 int main()
98 {
99 //read image file
100 src = imread("building.jpg");
101 if ( !src.data ) {
102 cout << "error read image" << endl;
103 return -1;
104 }
105
106 //create window
107 namedWindow(window_name);
108 imshow(window_name, src);
109
110
111 //create window for levels
112 namedWindow(levels_window);
113 levels_mat = Mat::ones(100,400, CV_8UC3);
114 levels_mat.setTo( Scalar(255,255,255) );
115 imshow(levels_window, levels_mat);
116
117 channelRead(0);
118 createTrackbar("Channel", levels_window, &channel, 3, callbackAdjustChannel);
119 createTrackbar("Shadow", levels_window, &Shadow, 255, callbackAdjust);
120 createTrackbar("Midtones", levels_window, &Midtones, 200, callbackAdjust);
121 createTrackbar("Highlight", levels_window, &Highlight, 255, callbackAdjust);
122 createTrackbar("OutShadow", levels_window, &OutputShadow, 255, callbackAdjust);
123 createTrackbar("OutHighlight", levels_window, &OutputHighlight, 255, callbackAdjust);
124
125 waitKey();
126
127 return 0;
128
129 }
运行效果:
原图:
先对红色通道(Channel = 1)调整各项色阶定义值,进行单通道色阶调整,效果如下:
再对RGB通道(Channel = 0)进行整体色阶调整,效果如下: