用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