zoukankan      html  css  js  c++  java
  • 用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.   
    8.   
    9. #include <cstdio>  
    10. #include <iostream>  
    11. #include "opencv2/core.hpp"  
    12. #include "opencv2/imgproc.hpp"  
    13. #include "opencv2/highgui.hpp"  
    14. #include "Curves.hpp"  
    15.   
    16. using namespace std;  
    17. using namespace cv;  
    18.   
    19. static string window_name = "Photo";  
    20. static Mat src;  
    21.   
    22. static string curves_window = "Adjust Curves";  
    23. static Mat curves_mat;  
    24. static int channel = 0;  
    25. Curves  curves;  
    26.   
    27. static void invalidate()  
    28. {  
    29.     curves.draw(curves_mat);  
    30.     imshow(curves_window, curves_mat);  
    31.   
    32.     Mat dst;  
    33.     curves.adjust(src, dst);  
    34.     imshow(window_name, dst);  
    35.   
    36.     int y, x;  
    37.     uchar *p;  
    38.   
    39.     y = 150; x = 50;  
    40.     p = dst.ptr<uchar>(y) + x * 3;  
    41.     cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";  
    42.   
    43.     y = 150; x = 220;  
    44.     p = dst.ptr<uchar>(y) + x * 3;  
    45.     cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";  
    46.   
    47.     y = 150; x = 400;  
    48.     p = dst.ptr<uchar>(y) + x * 3;  
    49.     cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  " << endl;  
    50. }  
    51.   
    52. static void callbackAdjustChannel(int , void *)  
    53. {  
    54.     switch (channel) {  
    55.     case 3:  
    56.         curves.CurrentChannel = &curves.BlueChannel;  
    57.         break;  
    58.     case 2:  
    59.         curves.CurrentChannel = &curves.GreenChannel;  
    60.         break;  
    61.     case 1:  
    62.         curves.CurrentChannel = &curves.RedChannel;  
    63.         break;  
    64.     default:  
    65.         curves.CurrentChannel = &curves.RGBChannel;  
    66.         break;  
    67.     }  
    68.   
    69.   
    70.     invalidate();  
    71. }  
    72.   
    73. static void callbackMouseEvent(int mouseEvent, int x, int y, int flags, void* param)  
    74. {  
    75.     switch(mouseEvent) {  
    76.     case CV_EVENT_LBUTTONDOWN:  
    77.         curves.mouseDown(x, y);  
    78.         invalidate();  
    79.         break;  
    80.     case CV_EVENT_MOUSEMOVE:  
    81.         if ( curves.mouseMove(x, y) )  
    82.             invalidate();  
    83.         break;  
    84.     case CV_EVENT_LBUTTONUP:  
    85.         curves.mouseUp(x, y);  
    86.         invalidate();  
    87.         break;  
    88.     }  
    89.     return;  
    90. }  
    91.   
    92.   
    93. int main()  
    94. {  
    95.     //read image file  
    96.     src = imread("building.jpg");  
    97.     if ( !src.data ) {  
    98.         cout << "error read image" << endl;  
    99.         return -1;  
    100.     }  
    101.   
    102.     //create window  
    103.     namedWindow(window_name);  
    104.     imshow(window_name, src);  
    105.   
    106.     //create Mat for curves  
    107.     curves_mat = Mat::ones(256, 256, CV_8UC3);  
    108.   
    109.     //create window for curves  
    110.     namedWindow(curves_window);  
    111.     setMouseCallback(curves_window, callbackMouseEvent, NULL );  
    112.     createTrackbar("Channel", curves_window, &channel,  3, callbackAdjustChannel);  
    113.   
    114.   
    115. // 范例:用程序代码在RedChannel中定义一条曲线  
    116. //  curves.RedChannel.clearPoints();  
    117. //  curves.RedChannel.addPoint( Point(10,  10) );  
    118. //  curves.RedChannel.addPoint( Point(240, 240) );  
    119. //  curves.RedChannel.addPoint( Point(127, 127) );  
    120.   
    121.     invalidate();  
    122.   
    123.     waitKey();  
    124.   
    125.     return 0;  
    126. }  

    运行效果如下:

    原图:

    对红色通道(Channel 1)进行曲线调整

    然后,对RGB通道(Channel 0)来一个经典的S型曲线调整

    呵呵,有点味道了

  • 相关阅读:
    JavaScript的关键点
    博客开始
    CCF
    java-CCF+杂七杂八
    【软件工程】需求分析V2再整理
    汇编 书上实验
    组原2
    chrome主页被绑架
    【软件工程】乱的一遭
    【我希望我能鼓起勇气】汇编语言
  • 原文地址:https://www.cnblogs.com/jukan/p/7058575.html
Copyright © 2011-2022 走看看