OpenCV實現Photoshop演算法(四): 色階調整
色階調整( 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物件有五個屬性值:
int Shadow; //輸入色階黑點值
float Midtones; //輸入色階灰點值(注意是浮點數)
int Highlight; //輸入色階白點值
int OutputShadow; //輸出色階黑點值
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)進行整體色階調整,效果如下: