1. 程式人生 > >用OpenCV實現Photoshop算法(三): 曲線調整

用OpenCV實現Photoshop算法(三): 曲線調整

快的 nes copy eve pla 很快 view 特點 色值

http://blog.csdn.net/c80486/article/details/52499919

系列文章:

用OpenCV實現Photoshop算法(一): 圖像旋轉

用OpenCV實現Photoshop算法(二): 圖像剪切

用OpenCV實現Photoshop算法(三): 曲線調整

用OpenCV實現Photoshop算法(四): 色階調整

用OpenCV實現Photoshop算法(五): 亮度對比度調整

用OpenCV實現Photoshop算法(六): 變為黑白圖像

用OpenCV實現Photoshop算法(七): 調整色相飽和度

用OpenCV實現Photoshop算法(八): 可選顏色

用OpenCV實現Photoshop算法(九): 高反差保留

三、曲線調整( Curves Adjustment )

曲線調整是Photoshop的最常用的重要功能之一。

網上關於曲線技術原理的材料都不完整。經過一個多月的探索、不斷實驗,我用OpenCV實現了曲線功能,基本算是揭開了“曲線之謎“。

(一)曲線原理

對於一個RGB圖像, 可以對R, G, B 通道進行獨立的曲線調整,即,對三個通道分別使用三條曲線(Curve)。還可以再增加一條曲線對 三個通道進行整體調整。 因此,對一個圖像,可以用四條曲線調整。最終的結果,是四條曲線調整後合並產生的結果。

我們先來分析對單通道一條曲線的原理,比如:對紅色通道定義一條曲線如下:

技術分享

圖中,橫軸是輸入,比左到右分別表示0到255. 縱軸是輸出,從下到上分別表示0到255.

該曲線由三個點定義,座標分別為: 點1(0,0), 點2(127,154),點3(255,255)

點1和點3是默認產生的, 點2是我們新增加的。在這三個點中畫出一條曲線(Spline).

調整的實現: 當輸入(紅色通道值)為X1時,將輸出值(新的紅色通道值)設為曲線對應的值 Y1.

代碼實現: 對圖片的所有像素點進行掃描, 取紅色值 X1, 換為 對應的 Y1. 其它兩個通道值(綠藍)不變。

比如: 像素點的RGB= (127, 230, 220), 其中紅色值為 X1 = 127, 對應曲線上的值Y1 = 154, 則對該通道曲線調整後 像素點的RGB= (154, 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類,實現曲線調整。

程序中定義了兩個窗口,一個是圖片窗口,一個是曲線窗口。

[cpp] view plain copy
  1. /*
  2. * test_Curves.cpp
  3. *
  4. * Created on: 2016年9月11日
  5. * Author: Administrator
  6. */
  7. #include <cstdio>
  8. #include <iostream>
  9. #include "opencv2/core.hpp"
  10. #include "opencv2/imgproc.hpp"
  11. #include "opencv2/highgui.hpp"
  12. #include "Curves.hpp"
  13. using namespace std;
  14. using namespace cv;
  15. static string window_name = "Photo";
  16. static Mat src;
  17. static string curves_window = "Adjust Curves";
  18. static Mat curves_mat;
  19. static int channel = 0;
  20. Curves curves;
  21. static void invalidate()
  22. {
  23. curves.draw(curves_mat);
  24. imshow(curves_window, curves_mat);
  25. Mat dst;
  26. curves.adjust(src, dst);
  27. imshow(window_name, dst);
  28. int y, x;
  29. uchar *p;
  30. y = 150; x = 50;
  31. p = dst.ptr<uchar>(y) + x * 3;
  32. cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ") ";
  33. y = 150; x = 220;
  34. p = dst.ptr<uchar>(y) + x * 3;
  35. cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ") ";
  36. y = 150; x = 400;
  37. p = dst.ptr<uchar>(y) + x * 3;
  38. cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ") " << endl;
  39. }
  40. static void callbackAdjustChannel(int , void *)
  41. {
  42. switch (channel) {
  43. case 3:
  44. curves.CurrentChannel = &curves.BlueChannel;
  45. break;
  46. case 2:
  47. curves.CurrentChannel = &curves.GreenChannel;
  48. break;
  49. case 1:
  50. curves.CurrentChannel = &curves.RedChannel;
  51. break;
  52. default:
  53. curves.CurrentChannel = &curves.RGBChannel;
  54. break;
  55. }
  56. invalidate();
  57. }
  58. static void callbackMouseEvent(int mouseEvent, int x, int y, int flags, void* param)
  59. {
  60. switch(mouseEvent) {
  61. case CV_EVENT_LBUTTONDOWN:
  62. curves.mouseDown(x, y);
  63. invalidate();
  64. break;
  65. case CV_EVENT_MOUSEMOVE:
  66. if ( curves.mouseMove(x, y) )
  67. invalidate();
  68. break;
  69. case CV_EVENT_LBUTTONUP:
  70. curves.mouseUp(x, y);
  71. invalidate();
  72. break;
  73. }
  74. return;
  75. }
  76. int main()
  77. {
  78. //read image file
  79. src = imread("building.jpg");
  80. if ( !src.data ) {
  81. cout << "error read image" << endl;
  82. return -1;
  83. }
  84. //create window
  85. namedWindow(window_name);
  86. imshow(window_name, src);
  87. //create Mat for curves
  88. curves_mat = Mat::ones(256, 256, CV_8UC3);
  89. //create window for curves
  90. namedWindow(curves_window);
  91. setMouseCallback(curves_window, callbackMouseEvent, NULL );
  92. createTrackbar("Channel", curves_window, &channel, 3, callbackAdjustChannel);
  93. // 範例:用程序代碼在RedChannel中定義一條曲線
  94. // curves.RedChannel.clearPoints();
  95. // curves.RedChannel.addPoint( Point(10, 10) );
  96. // curves.RedChannel.addPoint( Point(240, 240) );
  97. // curves.RedChannel.addPoint( Point(127, 127) );
  98. invalidate();
  99. waitKey();
  100. return 0;
  101. }

運行效果如下:

原圖:

技術分享

對紅色通道(Channel 1)進行曲線調整

技術分享

然後,對RGB通道(Channel 0)來一個經典的S型曲線調整

技術分享

呵呵,有點味道了

系列文章:

用OpenCV實現Photoshop算法(一): 圖像旋轉

用OpenCV實現Photoshop算法(二): 圖像剪切

用OpenCV實現Photoshop算法(三): 曲線調整

用OpenCV實現Photoshop算法(四): 色階調整

用OpenCV實現Photoshop算法(五): 亮度對比度調整

用OpenCV實現Photoshop算法(三): 曲線調整