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类,实现曲线调整。

程序中定义了两个窗口,一个是图片窗口,一个是曲线窗口。

  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算法(三): 曲线调整的更多相关文章

  1. 【VS开发】【图像处理】自动白平衡(AWB)算法---色温曲线

    原文地址:http://blog.csdn.net/wzwxiaozheng/article/details/38434391 白平衡算法---色温曲线 本文大体讲解了白平衡的算法流程,适用于想了解和 ...

  2. opencv学习笔记(三)基本数据类型

    opencv学习笔记(三)基本数据类型 类:DataType 将C++数据类型转换为对应的opencv数据类型 OpenCV原始数据类型的特征模版.OpenCV的原始数据类型包括unsigned ch ...

  3. 分布式共识算法 (三) Raft算法

    系列目录 分布式共识算法 (一) 背景 分布式共识算法 (二) Paxos算法 分布式共识算法 (三) Raft算法 分布式共识算法 (四) BTF算法 一.引子 1.1 介绍 Raft 是一种为了管 ...

  4. 基于OpenCV的KNN算法实现手写数字识别

    基于OpenCV的KNN算法实现手写数字识别 一.数据预处理 # 导入所需模块 import cv2 import numpy as np import matplotlib.pyplot as pl ...

  5. OpenCV实现KNN算法

    原文 OpenCV实现KNN算法 K Nearest Neighbors 这个算法首先贮藏所有的训练样本,然后通过分析(包括选举,计算加权和等方式)一个新样本周围K个最近邻以给出该样本的相应值.这种方 ...

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

    java 在centos6.5+eclipse环境下调用opencv实现sift算法,代码如下: import org.opencv.core.Core; import org.opencv.core ...

  7. OPENCV下SIFT算法使用方法笔记

    这几天继续在看Lowe大神的SIFT神作,看的眼花手脚抽筋.也是醉了!!!!实在看不下去,来点干货.我们知道opencv下自带SIFT特征检测以及MATCH匹配的库,这些库完全可以让我们进行傻瓜似的操 ...

  8. OpenCV中Camshitf算法学习(补充)

    结合OpenCV中Camshitf算法学习,做一些简单的补充,包括: 实现全自动跟随的一种方法 参考opencv中的相关demo,可以截取目标物体的图片,由此预先计算出其色彩投影图,用于实际的目标跟随 ...

  9. 基于深度学习的人脸识别系统(Caffe+OpenCV+Dlib)【三】VGG网络进行特征提取

    前言 基于深度学习的人脸识别系统,一共用到了5个开源库:OpenCV(计算机视觉库).Caffe(深度学习库).Dlib(机器学习库).libfacedetection(人脸检测库).cudnn(gp ...

随机推荐

  1. 【python】函数参数-任意参数

    def min1(args): res=args[0] for arg in args[1:]: if arg<res: res=arg return res def min2(first,re ...

  2. CentOS安装Oracle官方JRE

    CentOS自带的JRE是OpenJDK,因为一些原因,需要换用Oracle官方出品的JRE. Oracle JRE下载地址http://java.com/zh_CN/(下载时注意选择相应版本) 可以 ...

  3. 到底什么是hash

    1.什么是hash算法 Hash(散列.杂凑)算法,是把任意长度的输入通过特定的算法变换成固定长度的输出,输出的值就是hash值.这个特定的算法就叫hash算法,hash算法并不是一个固定不变的算法. ...

  4. Android多线程分析之中的一个:使用Thread异步下载图像

    Android多线程分析之中的一个:使用Thread异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可.转载请注明出处 打算整理一下对 Android Fr ...

  5. Office Developer Tools for Visual Studio 2012现在可用了

    [原文发表地址]   Now Available: Office Developer Tools for Visual Studio 2012 正如我以前写过的,我们正在为构建下一代Office和 S ...

  6. Python创建数组

    1  创建数组 array函数 >>> a=([1,2],[3,4]) >>> array(a) array([[1, 2], [3, 4]]) arange函数: ...

  7. 编写高质量代码--改善python程序的建议(四)

    原文发表在我的博客主页,转载请注明出处! 建议十八:有节制的使用from...import语句 python提供了三种方式引入外部模块: import语句 from...import... __imp ...

  8. 与Mysqli相关的四种数据库取值

    <!--取值方案一:通过数字数组 fetch_row()--><meta http-equiv="Content-Type" content="text ...

  9. 巨蟒python全栈开发-第10天 函数进阶

    一.今日主要内容总览(重点) 1.动态传参(重点) *,** *: 形参:聚合 位置参数*=>元组 关键字**=>字典 实参:打散 列表,字符串,元组=>* 字典=>** 形参 ...

  10. CSRF Cross-site request forgery

    w 跨站请求伪造目标站---无知用户---恶意站 http://fallensnow-jack.blogspot.com/2011/08/webgoat-csrf.html https://wiki. ...