1. 程式人生 > >用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類,實現曲線調整。

程式中定義了兩個視窗,一個是圖片視窗,一個是曲線視窗。

/*
 * test_Curves.cpp
 *
 *  Created on: 2016年9月11日
 *      Author: Administrator
 */


#include <cstdio>
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "Curves.hpp"

using namespace std;
using namespace cv;

static string window_name = "Photo";
static Mat src;

static string curves_window = "Adjust Curves";
static Mat curves_mat;
static int channel = 0;
Curves  curves;

static void invalidate()
{
	curves.draw(curves_mat);
	imshow(curves_window, curves_mat);

	Mat dst;
	curves.adjust(src, dst);
	imshow(window_name, dst);

	int y, x;
	uchar *p;

	y = 150; x = 50;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";

	y = 150; x = 220;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  ";

	y = 150; x = 400;
	p = dst.ptr<uchar>(y) + x * 3;
	cout << "(" << int(p[2]) << ", " << int(p[1]) << ", " << int(p[0]) << ")  " << endl;
}

static void callbackAdjustChannel(int , void *)
{
	switch (channel) {
	case 3:
		curves.CurrentChannel = &curves.BlueChannel;
		break;
	case 2:
		curves.CurrentChannel = &curves.GreenChannel;
		break;
	case 1:
		curves.CurrentChannel = &curves.RedChannel;
		break;
	default:
		curves.CurrentChannel = &curves.RGBChannel;
		break;
	}


	invalidate();
}

static void callbackMouseEvent(int mouseEvent, int x, int y, int flags, void* param)
{
	switch(mouseEvent) {
	case CV_EVENT_LBUTTONDOWN:
		curves.mouseDown(x, y);
		invalidate();
		break;
	case CV_EVENT_MOUSEMOVE:
		if ( curves.mouseMove(x, y) )
			invalidate();
		break;
	case CV_EVENT_LBUTTONUP:
		curves.mouseUp(x, y);
		invalidate();
		break;
	}
	return;
}


int main()
{
	//read image file
	src = imread("building.jpg");
	if ( !src.data ) {
		cout << "error read image" << endl;
		return -1;
	}

	//create window
	namedWindow(window_name);
	imshow(window_name, src);

	//create Mat for curves
	curves_mat = Mat::ones(256, 256, CV_8UC3);

	//create window for curves
	namedWindow(curves_window);
	setMouseCallback(curves_window, callbackMouseEvent, NULL );
	createTrackbar("Channel", curves_window, &channel,  3, callbackAdjustChannel);


// 範例:用程式程式碼在RedChannel中定義一條曲線
//	curves.RedChannel.clearPoints();
//	curves.RedChannel.addPoint( Point(10,  10) );
//	curves.RedChannel.addPoint( Point(240, 240) );
//	curves.RedChannel.addPoint( Point(127, 127) );

	invalidate();

	waitKey();

	return 0;
}

執行效果如下:

原圖:


對紅色通道(Channel 1)進行曲線調整


然後,對RGB通道(Channel 0)來一個經典的S型曲線調整


呵呵,有點味道了

相關推薦

OpenCV實現Photoshop演算法(): 曲線調整

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 三、曲線調整( Curves Adjustment ) 曲線調整是Photoshop的最常用的重要功能之一。 網上關於曲線技術原理的材料都不完整。經過一個多月的探索、不斷實驗

OpenCV實現Photoshop演算法(七): 調整色相飽和度

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 七、調整色相飽和度 Photoshop 的色相/飽和度調整,可以對全圖、紅、黃、綠、青、藍、洋紅六個通道進行設定。 每個通道可設定: 色相(hue),  飽和度(satuat

OpenCV實現Photoshop演算法(十): 美白磨皮(未完)

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 十、人像美白磨皮 人像美白磨皮是一個複雜的綜合的高技術活。 基本過程是這樣的:  選取面板區域-〉磨皮-〉美白-〉銳化 相關工作涉及多種演算法: 目前我尚在研究中,有的演算法寫

OpenCV實現Photoshop演算法(九): 高反差保留

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 九、高反差保留(High Pass) 高反差保留是一個Photoshop的濾鏡功能。常見的用法是用於銳化,對影象進行1個畫素的高反差保留,再將得到的新影象以強光混合方式疊加在原圖上

OpenCV實現Photoshop演算法(二): 影象剪下

系列文章: 用OpenCV實現Photoshop演算法(四): 色階調整 二、影象剪下 用OpenCV 寫一個影象剪下函式 imageCrop() 如下: //影象剪下 //引數:src為源影象, dst為結果影象, rect為剪下區域 //返回

OpenCV實現Photoshop算法(): 曲線調整

快的 nes copy eve pla 很快 view 特點 色值 http://blog.csdn.net/c80486/article/details/52499919 系列文章: 用OpenCV實現Photoshop算法(一): 圖像旋轉 用OpenCV實現Photo

Python調OpenCV實現人臉識別

source display document down char name 實現 cvt config [硬件環境] Win10 64位 [軟件環境] Python版本:2.7.3 IDE:JetBrains PyCharm 2016.3.2 Python庫: 1.1)

Python調OpenCV實現攝像頭的運動檢測

搭建過程 .get mes bsdiff 資源 read del con 函數 [硬件環境] Win10 64位 [軟件環境] Python版本:2.7.3 IDE:JetBrains PyCharm 2016.3.2 Python庫: 1.1) opencv-python

Python調OpenCV實現攝像頭的運動檢測[樹莓派版]

then see pip port wid warning number 12px ram [硬件環境] RaspberryPi 3代B型(英國版) [軟件環境] 操作系統:Raspbian Python版本:2.7.3 Python庫: 1.1) opencv-pytho

0004-OpenCV實現影象平移的程式碼(分影象尺寸不變和變兩種情況)

影象平移是啥東西就不用講了吧!需要注意的是影象平移有兩種,第一種是平移後圖像大小不變,這樣會損失影象的部分;第二種是平移後圖像大小變化,這樣原影象不會有損失。 直接上程式碼,大家看效果吧! 程式碼流程如下: 讀取影象→顯示原影象→呼叫自定義的函式translateTransform,作平移後

opencv實現圖片顏色反轉

# 灰度圖片顏色翻轉效果 import cv2 import numpy as np img = cv2.imread("1.jpg",1) imgInfo = img.shape height = imgInfo[0] width = imgInfo[1] gray = cv2.cvtColor

opencv實現圖片的仿射變換和旋轉

import cv2 import numpy as np img = cv2.imread('1.jpg',1) cv2.imshow('old',img) imgInfo = img.shape height = imgInfo[0] width = imgInfo[1] matSrc = n

QT 下opencv實現影象分類(1)

一.概述 1.按影象中的內容給影象分類是計算機視覺中比較適合初學者的專案,我見過好多手機相簿都有這一個功能,比如把美食歸為一個標籤,藍天白雲歸為一個標籤等等。還有我之前做過的車牌識別的專案都用到影象分類。 2.我做這個專案的環境是QT加opencv3.2,專案在MAC上跑

一個Java實現密碼演算法,使用socket引發的血案

public static void main(String[] args) throws IOException, ParseException { ServerSocket serverSocket = new ServerSocket(1

RelativeLayout實現左中右部分顯示

http://mingkg21.iteye.com/blog/588214 有人問到如何實現這樣的佈局顯示 實現這樣的佈局應該有很多種方式很多人都會了。既然有人問了,那肯定有的人還不知道怎麼實現。那分享我的實現方式吧。 我習慣用RelativeLayout,用Ta

DirectX實現魔方()視角變換及縮放(附原始碼)

在本系列第一篇介紹過滑鼠按鍵的功能,如下。 左鍵拖拽 - 旋轉魔方 右鍵拖拽 - 變換視角 滾輪 - 縮放魔方 今天研究一下如何實現後面兩個功能,用到的技術主要是Arcball,Arcball是實現Model-View-Camera的重要技術,這裡的旋轉基於Quaternion(四元數)來實現

OpenCV實現目標追蹤的八種方法(轉)

原文地址:http://m.elecfans.com/article/722414.html 編者按:目標跟蹤作為機器學習的一個重要分支,加之其在日常生活、軍事行動中的廣泛應用,很多國內外學者都對此頗有研究。本文將討論OpenCV上八種不同的目標追蹤演算法。

opencv實現立體匹配

轉自:http://blog.csdn.net/scyscyao/article/details/5443341 嘗試用OpenCV來實現立體視覺也有一段時間了,主要的參考資料就是Learning OpenCV十一、十二章和OpenCV論壇上一些前輩的討論。過程中磕磕碰碰,

機器學習 使用python+OpenCV實現knn演算法手寫數字識別

基本上照搬了http://lib.csdn.net/article/opencv/30167的程式碼,只是改了一點bug和增加了一點功能輸入就是直接在一個512*512大小的白色畫布上畫黑線,然後轉化為01矩陣,用knn演算法找訓練資料中最相近的k個,現在應該是可以對所有字元

作業系統--JavaScript實現銀行家演算法

var num_process; //記錄程序數 var num_resource;//記錄資源數 var max = new Array();//最大資源數 var need = new Array();//資源需求數 var work = new Array();//資源可用數 var work2 = n