您好,欢迎来到知库网。
搜索
您的当前位置:首页OpenCV实现Photoshop算法(三):曲线调整

OpenCV实现Photoshop算法(三):曲线调整

来源:知库网
OpenCV实现Photoshop算法(三):曲线调整

曲线调整( Curves Adjustment )

曲线调整是Photoshop的最常⽤的重要功能之⼀。

⽹上关于曲线技术原理的材料都不完整。经过⼀个多⽉的探索、不断实验,我⽤OpenCV实现了曲线功能,基本算是揭开了“曲线之谜“。(⼀)曲线原理

对于⼀个RGB图像, 可以对R, G, B 通道进⾏独⽴的曲线调整,即,对三个通道分别使⽤三条曲线(Curve)。还可以再增加⼀条曲线对 三个通道进⾏整体调整。 因此,对⼀个图像,可以⽤四条曲线调整。最终的结果,是四条曲线调整后合并产⽣的结果。

我们先来分析对单通道⼀条曲线的原理,⽐如:对红⾊通道定义⼀条曲线如下:

图中,横轴是输⼊,⽐左到右分别表⽰0到255. 纵轴是输出,从下到上分别表⽰0到255.该曲线由三个点定义,座标分别为: 点1(0,0), 点2(127,1),点3(255,255)点1和点3是默认产⽣的, 点2是我们新增加的。在这三个点中画出⼀条曲线(Spline).

调整的实现: 当输⼊(红⾊通道值)为X1时,将输出值(新的红⾊通道值)设为曲线对应的值 Y1.代码实现: 对图⽚的所有像素点进⾏扫描, 取红⾊值 X1, 换为 对应的 Y1. 其它两个通道值(绿蓝)不变。

⽐如: 像素点的RGB= (127, 230, 220), 其中红⾊值为 X1 = 127, 对应曲线上的值Y1 = 1, 则对该通道曲线调整后 像素点的RGB= (1, 230, 220)

如果曲线仅是⼀条由左下⾓到右上⾓的45度斜线,则 X1 总是等于 Y1, 则曲线调整后 图⽚不变。对红、绿、蓝三个独⽴通道调整⽅式都与上述算法相同。各通道调整是互不相关的。然后,我们再来分析对RGB通道进⾏整体调整的原理。

⽐如: 像素点的RGB= (127, 230, 220), 对RGB通道进⾏整体调整, 则根据该曲线同时对R, G, B三个值进⾏调整。 R = 127 作为输⼊值, 计算曲线上的 对应输出值 R1 G = 230作为输⼊值, 计算曲线上的 对应输出值 G1 B = 220作为输⼊值, 计算曲线上的 对应输出值 B1 则新的像素点的RGB =(R1, G1, B1)

⽤⼏条曲线同时调整时,先对红、绿、蓝三个独⽴通道分别进⾏调整,最后对RGB总通道进⾏调整。由于曲线调整仅仅是数值替换,可以⽤⼀个转换表进⾏快速运算, 因此,曲线调整的速度是很快的。

(⼆)曲线的⽣成

Photoshop使⽤的曲线是⼀种SPline 曲线。这种曲线表现⼒很强,特点是:仅需要定义⼏个控制点,就可以定义⼀条平滑的曲线,且曲线同时通过所有控制点。⽣成曲线时,只需要给出⼏个控制点,调⽤曲线⽣成函数即可。SPline的具体数学原理我就不讲了,⽣成函数可以看下⾯的源码Curves.cpp中的spline()函数(三)曲线调整的OpenCV实现

我⽤opencv写了两个 C++ 类: Curves类实现了多通道的曲线的定义、绘制、实施调整。 Curve类是⼀个通道的曲线定义类。

源码共两个⽂件: Curves.hpp, Curves.cpp

源码有⼀定的长度,不具体解释了,请见注释。补充说明⼏点:

1, Curves类中定义了四个Curve对象(即四个通道),分别是RedChannel, GreenChannel, BlueChannel 和 RGBChannel.2, Curves类⽀持⽤⿏标⽣成曲线,使⽤⽅法见例程。

2, Curves.cpp中的spline()函数是⽣成曲线数值的,即输⼊⼀串控制点,通过插值运算,⽣成⼀系列的输出值。 3, 除了⽤⿏标⽣成曲线以外, 也可以⽤程序代码直接⽣成曲线:

先使⽤Curve类的clearPoints()⽅法清除所有控制点,再调⽤addPoint()⽅法逐个添加控制点即可。(四)例程

写⼀个例程,使⽤Curves类,实现曲线调整。

程序中定义了两个窗⼝,⼀个是图⽚窗⼝,⼀个是曲线窗⼝。 1 /*

2 * test_Curves.cpp 3 *

4 * Created on: 2016年9⽉11⽇ 5 * Author: Administrator 6 */ 7 8

9 #include 10 #include

11 #include \"opencv2/core.hpp\" 12 #include \"opencv2/imgproc.hpp\" 13 #include \"opencv2/highgui.hpp\" 14 #include \"Curves.hpp\" 15

16 using namespace std; 17 using namespace cv; 18

19 static string window_name = \"Photo\"; 20 static Mat src; 21

22 static string curves_window = \"Adjust Curves\"; 23 static Mat curves_mat; 24 static int channel = 0; 25 Curves curves; 26

27 static void invalidate() 28 {

29 curves.draw(curves_mat);

30 imshow(curves_window, curves_mat); 31

32 Mat dst;

33 curves.adjust(src, dst);

34 imshow(window_name, dst); 35

36 int y, x; 37 uchar *p; 38

39 y = 150; x = 50;

40 p = dst.ptr(y) + x * 3;

41 cout << \"(\" << int(p[2]) << \ << int(p[1]) << \ << int(p[0]) << \") \"; 42

43 y = 150; x = 220;

44 p = dst.ptr(y) + x * 3;

45 cout << \"(\" << int(p[2]) << \ << int(p[1]) << \ << int(p[0]) << \") \"; 46

47 y = 150; x = 400;

48 p = dst.ptr(y) + x * 3;

49 cout << \"(\" << int(p[2]) << \ << int(p[1]) << \ << int(p[0]) << \") \" << endl; 50 } 51

52 static void callbackAdjustChannel(int , void *) 53 {

switch (channel) { 55 case 3:

56 curves.CurrentChannel = &curves.BlueChannel; 57 break; 58 case 2:

59 curves.CurrentChannel = &curves.GreenChannel; 60 break; 61 case 1:

62 curves.CurrentChannel = &curves.RedChannel; 63 break; default:

65 curves.CurrentChannel = &curves.RGBChannel; 66 break; 67 } 68 69

70 invalidate(); 71 } 72

73 static void callbackMouseEvent(int mouseEvent, int x, int y, int flags, void* param) 74 {

75 switch(mouseEvent) {

76 case CV_EVENT_LBUTTONDOWN: 77 curves.mouseDown(x, y); 78 invalidate(); 79 break;

80 case CV_EVENT_MOUSEMOVE: 81 if ( curves.mouseMove(x, y) ) 82 invalidate(); 83 break;

84 case CV_EVENT_LBUTTONUP: 85 curves.mouseUp(x, y); 86 invalidate(); 87 break; 88 }

return; 90 } 91

92

93 int main() 94 {

95 //read image file

96 src = imread(\"building.jpg\"); 97 if ( !src.data ) {

98 cout << \"error read image\" << endl; 99 return -1;100 }101

102 //create window

103 namedWindow(window_name);104 imshow(window_name, src);105

106 //create Mat for curves

107 curves_mat = Mat::ones(256, 256, CV_8UC3);108

109 //create window for curves

110 namedWindow(curves_window);

111 setMouseCallback(curves_window, callbackMouseEvent, NULL );

112 createTrackbar(\"Channel\", curves_window, &channel, 3, callbackAdjustChannel);113 114

115 // 范例:⽤程序代码在RedChannel中定义⼀条曲线116 // curves.RedChannel.clearPoints();

117 // curves.RedChannel.addPoint( Point(10, 10) );118 // curves.RedChannel.addPoint( Point(240, 240) );119 // curves.RedChannel.addPoint( Point(127, 127) );120

121 invalidate();122

123 waitKey();124

125 return 0;126 }

运⾏效果如下:原图:

对红⾊通道(Channel 1)进⾏曲线调整

然后,对RGB通道(Channel 0)来⼀个经典的S型曲线调整

呵呵,有点味道了

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- zicool.com 版权所有 湘ICP备2023022495号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务