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对象有五个属性值:

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)进行整体色阶调整,效果如下:


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM