1. 程式人生 > >學習OpenCV範例(五)——改變影象的對比度和亮度

學習OpenCV範例(五)——改變影象的對比度和亮度

學習到範例五的時候,發覺雖然範例都很簡單,但是做記錄的時候,並且把程式裡面使用過的類或方法都弄明白,也就不簡單了,接下來介紹一下範例五吧。

1、影象處理

一般來說,影象處理運算元是帶有一幅或多幅輸入影象、產生一幅輸出影象的函式。

影象變換可分為以下兩種:

點運算元(畫素變換):影象對比度和亮度,等等

鄰域(基於區域的)運算元:均值濾波,中值濾波,等等,也就是卷積運算

2、亮度和對比度調整

兩種常用的點過程(即點運算元),是用常數對點進行 乘法 和 加法 運算:

                                                                                                                      g(x) = \alpha f(x) + \beta

兩個引數 \alpha > 0 和 \beta 一般稱作 增益 和 偏置 引數。我們往往用這兩個引數來分別控制 對比度 和 亮度 。

你可以把 f(x) 看成源影象畫素,把 g(x) 看成輸出影象畫素。這樣一來,上面的式子就能寫得更清楚些:

                                                                                                                  g(i,j) = \alpha \cdot f(i,j) + \beta

其中, i 和 j 表示畫素位於 第i行 和 第j列 。

3、執行程式碼如下:

程式使用三種方式來實現亮度和對比度的調整,並且輸出了各自運算的時間。

#include "stdafx.h"


#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

double alpha; /**< 控制對比度 */
int beta;  /**< 控制亮度 */

int main( int argc, char** argv )
{
	double t;
	/// 讀入使用者提供的影象
	Mat image = imread( "Lena.jpg" );
	Mat new_image = Mat::zeros( image.size(), image.type() );
	Mat new_image1 = Mat::zeros( image.size(), image.type() );
	Mat new_image2 = Mat::zeros( image.size(), image.type() );


	/// 初始化
	cout << " Basic Linear Transforms " << endl;
	cout << "-------------------------" << endl;
	cout << "* Enter the alpha value [1.0-3.0]: ";
	cin >> alpha;
	cout << "* Enter the beta value [0-100]: ";
	cin >> beta;

    t = (double)getTickCount();
	/// 執行運算 new_image(i,j) = alpha*image(i,j) + beta
	for( int y = 0; y < image.rows; y++ )
	{
		for( int x = 0; x < image.cols; x++ )
		{
			for( int c = 0; c < 3; c++ )
			{
				new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
			}
		}
	}
	t = 1000*((double)getTickCount() - t)/getTickFrequency();
	cout << ".at+[] Times passed in milliseconds: " << t << endl;


    t = (double)getTickCount();
	int nr= image.rows; // number of rows  
	int nc= image.cols * image.channels(); // total number of elements per line  
	for (int y=0; y<nr; y++) 
	{  
		uchar* data= image.ptr<uchar>(y);  
		uchar* data1=new_image1.ptr<uchar>(y);
		for (int x=0; x<nc; x++) 
		{  
			data1[x]=saturate_cast<uchar>(alpha*data[x]+beta);
		}
	}
	t = 1000*((double)getTickCount() - t)/getTickFrequency();
	cout << ".ptr+[] Times passed in milliseconds: " << t << endl;

	t = (double)getTickCount();
	image.convertTo(new_image2, -1, alpha, beta);
	t = 1000*((double)getTickCount() - t)/getTickFrequency();
	cout << "convertTo Times passed in milliseconds: " << t << endl;


	/// 建立視窗
	namedWindow("Original Image", 1);
	namedWindow("New Image", 1);
	namedWindow("New Image1", 1);
	namedWindow("New Image2", 1);

	/// 顯示影象
	imshow("Original Image", image);
	imshow("New Image", new_image);
	imshow("New Image1", new_image1);
	imshow("New Image2", new_image2);

	/// 等待使用者按鍵
	waitKey();
	return 0;
}

4、執行結果:

  

           圖1、原圖                       圖2、.at方法

 

          圖3、.ptr方法                    圖4、convertTo方法


                            圖5、執行時間

5、結論

從執行時間可以看出,使用OpenCV自帶的函式執行效率最高,而使用.ptr方法比.at方法好,這也印證了前面部落格學習OpenCV範例(二)——OpenCV如何掃描影象、利用查詢表和計時所說的。

6、用到的類

convertTo:

功能:通過縮放比例將陣列變換成其他型別

結構:

void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0 ) const
m:輸出影象

rtype:輸出影象的型別

alpha:比例因子α,決定對比度

beta:附加值β,決定亮度

函式原理如下:

                  m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) +  \beta )

saturate_cast:

功能:防止資料溢位

為什麼上面的函式會用到saturate_cast呢,因為無論是加是減,乘除,都會超出一個畫素灰度值的範圍(0~255)所以,所以當運算完之後,結果為負,則轉為0,結果超出255,則為255。