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

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算法(五): 亮度对比度调整

时间: 2024-07-31 15:13:29

用OpenCV实现Photoshop算法(三): 曲线调整的相关文章

用OpenCV实现Photoshop算法(一): 图像旋转

最近学习了OpenCV,于是想用它实现Photoshop的主要功能,用于照片处理. 对于一张照片,PS的一般处理步骤包括: 1, 旋转图片,校正位置. 2,剪切,调整大小,重新构图. 3,调整色阶.曲线,使图片曝光正确.对比适中. 4,调整对比度.饱和度 5,印章去掉不想要的东西,液化调整形体线条 6,对于人像图片,美肤.美白 7, 用色彩平衡.可选颜色等调整色调,形成照片调性 8,加一些光效 9,锐化 以后的一系列博文将采用OpenCV逐一实现Photoshop的算法和功能, 并用计算机视觉人

PhotoShop算法实现进阶-模糊滤镜-高斯滤波(二十三)

PhotoShop算法实现进阶-模糊滤镜-高斯滤波(二十三) [email protected] http://blog.csdn.net/kezunhai 高斯模糊(Gaus Blur)采用二维高斯模板对图像进行模糊处理,用于图像模糊化(去除细节和噪声),它的处理效果给人一种更佳柔和的感觉. 一维高斯: 二维高斯: 理论上,高斯分布在所有定义域上都有非负值,这就需要一个无限大的卷积核.实际上,仅需要取均值周围3倍标准差内的值,以外部份直接去掉即可. 如下图为一个标准差为1.0的整数值高斯核:

Dijkstra算法(三)之 Java详解

前面分别通过C和C++实现了迪杰斯特拉算法,本文介绍迪杰斯特拉算法的Java实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 迪杰斯特拉算法介绍 迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想

数学图形(1.50)三曲线

很神秘的一种曲线,从网上搜索,发现在某一大人物的介绍中有如下说明: 自幼即聪慧异常,在校成绩,每列前茅,尤长数学,为全级冠,恃相对论,每辩必胜,创三 曲线,得博士衔. 这三曲线到底是什么样的图形?让我开下脑洞,揣测一下.可能是一种类似奔驰车标的图形.其 极坐标方程为:r = 1/(1 - (mod(t*3/PI, 2) - 1)*(mod(t*3/PI, 2) - 1))其中t的取值范围是0到2*PI图形如下: 生成代码如下: vertices = 1000 t = from 0 to (2*P

Kruskal算法(三)之 Java详解

前面分别通过C和C++实现了克鲁斯卡尔,本文介绍克鲁斯卡尔的Java实现. 目录 1. 最小生成树 2. 克鲁斯卡尔算法介绍 3. 克鲁斯卡尔算法图解 4. 克鲁斯卡尔算法分析 5. 克鲁斯卡尔算法的代码说明 6. 克鲁斯卡尔算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的

OpenCV使用Harris算法实现角点检测

纯粹阅读,请移步OpenCV使用Harris算法实现角点检测 效果图 源码 KqwOpenCVFeaturesDemo 角点是两条边缘的交点或者在局部邻域中有多个显著边缘方向的点.Harris角点检测是一种在角点检测中最常见的技术. Harris角点检测器在图像上使用滑动窗口计算亮度的变化. 封装 这里用到了RxJava.主要是因为图片处理是耗时操作,会阻塞线程,为了防止界面卡顿,这里使用RxJava进行了线程切换. /** * Harris角点检测 * * @param bitmap 要检测的

java 在centos6.5+eclipse环境下调用opencv实现sift算法

java 在centos6.5+eclipse环境下调用opencv实现sift算法,代码如下: import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfKeyPoint; import org.opencv.highgui.Highgui; import org.opencv.features2d.*; public class ExtractSIFT{ public static

图解缓存淘汰算法三之FIFO

1.概念分析 FIFO(First In First Out),即先进先出.最先进入的数据,最先出来.一个很简单的算法.只要使用队列数据结构即可实现.那么FIFO淘汰算法基于的思想是"最近刚访问的,将来访问的可能性比较大".看了这三个算法,大家对淘汰的理解,我想一定深入了不少,基于一种对未来可能性的推断上. 2.原理分析 FIFO的淘汰过程与传送带的方式类似,最先放到传送带上的数据会最先被淘汰.如下图中的A. 3.优略分析 [命中率] 命中率较低,不推荐使用. [复杂度] 非常简单.

OPENCV下SIFT算法使用方法笔记

这几天继续在看Lowe大神的SIFT神作,看的眼花手脚抽筋.也是醉了!!!!实在看不下去,来点干货.我们知道opencv下自带SIFT特征检测以及MATCH匹配的库,这些库完全可以让我们进行傻瓜似的操作.但实际用起来的时候还不是那么简单.下文将对一个典型的基于OPENCV的SIFT特征点提取以及匹配的例程进行分析,并由此分析详细的对OPENCV中SIFT算法的使用进行一个介绍. OPENCV下SIFT特征点提取与匹配的大致流程如下: 读取图片->特征点检测(位置,角度,层)->特征点描述的提取