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. bgr to rgb

    因为在研究车牌识别算法(plr),遇到了算法 处理的格式问题,可分三个常用格式: 0:rgb 1:bgr 2:yuv422——需要注意的是,这里为啥选yuv422做识别,当然还可选yuv444,最坏打 ...

  2. Java WEB 之页面间传递特殊字符

    本文是学习网络上的文章时的总结以及自己的一点实践.感谢大家无私的分享. 昨天在做项目的时候,有一个页面间传递特殊字符的需求,查了一些资料.如今将自己的经验写出来. 首先.在前台编码 var fckPu ...

  3. 富文本编辑期Quill

    官方网站http://quilljs.com/ 使用方法 <!-- Create the toolbar container --> <div id="toolbar&qu ...

  4. WebSocket入门教程(五)-- WebSocket实例:简单多人聊天室

    from:https://blog.csdn.net/u010136741/article/details/51612594 [总目录]   WebSocket入门教程--大纲   [实例简介]   ...

  5. 1.2_php验证码

    使用php生成动态的验证码图片 <!DOCTYPE html> <html> <head> <meta charset="utf-8"&g ...

  6. 《JAVA多线程编程核心技术》 笔记:第四章、Lock的使用

    一.使用ReentrantLock类1.1 ReentrantLock的使用:1.2 ReentrantLock的不足:1.3 正确使用Condition实现等待/通知1.4 使用多个Conditio ...

  7. node.js开发学习一HelloWorld

    前言:由于公司业务需求,最近启动了node.js的开发任务,想把自己的开发学习历程记录记录下来,可以增加记忆,也方便查找.虽然对javascript有一定的了解,但是刚接触node.js的时候,发现还 ...

  8. Powered by Flink

    Apache Flink: Powered by Flink https://flink.apache.org/poweredby.html Powered by Flink Apache Flink ...

  9. python list中append()与extend()用法

    列表是以类的形式实现的.“创建”列表实际上是将一个类实例化.因此,列表有多种方法可以操作. 1. 列表可包含任何数据类型的元素,单个列表中的元素无须全为同一类型. 2. append() 方法向列表的 ...

  10. Java基础语法 - 面向对象 - this 关键字

    在Java语言中规定使用this关键字来代表本类对象的引用,this关键字被隐式地用于引用对象的成员变量和方法. this关键字引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,就要添加 ...