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. 64位系统下,一个32位的程序究竟可以申请到多少内存,4GB还是更多?(一)

    前言: cpu的位是指一次性可处理的数据量是多少,1字节=8位,32位处理器可以一次性处理4个字节的数据量,依次类推.32位操作系统针对的32位的CPU设计.64位操作系统针对的64位的CPU设计.操 ...

  2. convolutional neural network 课程笔记

    一.CNN基础 (1)CNN在CV方面的应用 image classification(图像识别).object detection(目标检测).neural style transfer(风格迁移) ...

  3. 数码相机常用CCD/CMOS尺寸对比

    数码相机的关键元件CCD或CMOS又称为“影像传感器”,其作用相当于感光胶片.CCD尺寸越大,采集光线的效果越好,画面记录的信息就越多,保留的细节也就越丰富,所以图像更完美漂亮. CCD尺寸的大小与像 ...

  4. iOS开发之--storyboary下,为btn/lab/view等添加裁剪和阴影的方法

    在开发过程中,很多时候我们都需要定制一个控件的属性,比如边框.阴影.圆角裁剪等,使用代码创建控件的时候,我们都知道,直接对layer图层进行各种属性的操作, self.button.layer.cor ...

  5. MySQL中的日期和时间函数

    常用日期函数如下: 函   数 功   能 CURDATE() 获取当前日期 CURTIME() 获取当前时间 NOW() 获取当前的日期和时间 UNIX_TIMESTAMP(date) 获取日期的U ...

  6. Volley使用详细说明

    前一篇粗略的介绍了一下Volley,并在最后附上了一段使用代码,这一篇详细的介绍一下Volley的使用.前面也说了Volley主要获取JSON对象和图片加载,这里也分为两部分介绍. 1.获取JSON对 ...

  7. mysql数据库导入到oracle数据库

    首先,写一个cmd脚本 xx.cmd sqlldr username/password control=xx.ctl errors=10000000 direct=y 再写一个bat脚本xx.bat ...

  8. Openstack块存储cinder安装配置

    openstack service create --name cinderv2 \ --description "OpenStack Block Storage" volumev ...

  9. Python全栈day24(面向对象编程作业作业_定义学校老师课程班级学生类)

    面向对象作业 作业_定义学校老师课程班级学生类.py #面向对象编程作业,定义学校老师课程班级学生类 #定义几个类,尽可能定义多的数据属性及函数属性 class School: def __init_ ...

  10. Powershell&.NET数值取整处理

    如何取一个数的整数值? 使用类型强制转换 Powershell的强制转换有2种方式,一种是直接类型强制转换,另一种是通过-as运算符进行转换 PS F:\> [int] (3 / 2) # 直接 ...