1. 程式人生 > 實用技巧 >OpenCV實現Photoshop演算法(四): 色階調整

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)進行整體色階調整,效果如下: