目录简介Example运行截图Example分析1.调节亮度和对比度 1.1源码 1.2源码分析-使用convertTo调节图像亮度和对比度2.绘制图像直方图 2.1源码 2.2源码分析 2.2.1使用calcHist计算直方图 2.2.2使用Mat::ones创建Mat 2.2.3使用normalize函数归一化 2.2.4使用cvRound四舍五入
3.创建滚动条调整参数
Example代码
简介
本文记录了对OpenCV示例dedmhist.cpp的分析。 资料地址: http://docs.opencv.org/3.0.0/d2/dcb/demhist_8cpp-example.html
首先这个示例非常有意义,因为OpenCV和很多图像库不同,并未明确提供对亮度和对比度操作的接口,看了这个示例后就可以搞定。
整个示例可以总结为(1)调节图像亮度对比度; (2)绘制图像直方图; (3)创建滚动条调整参数。
Example运行截图
brightness | contrast | image | histogram |
100 | 100 | ||
200 | 100 | ||
100 | 200 |
Example分析 1.调节图像亮度对比度 1.1源码 image.convertTo(dst, CV_8U, a, b); 1.2源码分析-使用convertTo条件图像对比度和亮度 如之前所讲,在OpenCV中无法直接找到如Light(亮度)或Contrast(对比度)字样的接口, 其功能通过函数convertTo实现。
cv::Mat::convertTo 这个函数提供点算子(像素变换)能力,通过增益(alpha)和偏置(beta)参数对图像进行调整,我们也可以使用它完成亮度(beta)和对比度(alpha)的调整,其公式如下 函数原型: void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const; 参数说明: m:输出矩阵;如果在使用前没有声明或需要修改,会自行分配(需要赞美的能力!)。 rtype:新的矩阵类型。因此也有人使用这个函数进行类型转换 alpha:增益参数,对比度 beta:偏置参数,亮度 PS:convertTo属于Mat的成员函数。 PS2:saturate_cast用于防止溢出,结果小于0则转为0,大于255,则转为255。 |
2.绘制图像直方图 2.1源码 calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, 0);Mat histImage = Mat::ones(200, 320, CV_8U)*255;normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, CV_32F);histImage = Scalar::all(255);int binW = cvRound((double)histImage.cols/histSize);for( int i = 0; i < histSize; i++ ) rectangle( histImage, Point(i*binW, histImage.rows), Point((i+1)*binW, histImage.rows - cvRound(hist.at<float>(i))), Scalar::all(0), -1, 8, 0 );
2.2源码分析 2.2.1使用calcHist计算直方图 OpenCV提供calcHist计算直方图
calcHist 函数原型:void calcHist( const Mat* images, int nimages, const int* channels, InputArray mask, OutputArray hist, int dims, const int* histSize, const float** ranges, bool uniform = true, bool accumulate = false );参数说明: arrays:输入的图像的指针,可以是多幅图像,所有的图像必须有同样的深度(CV_8U or CV _32F)。 同时一副图像可以有多个channes。 nimages:输入的图像的个数。 channels:用来计算直方图的channes的数组。比如输入是2副图像,第一副图像有0,1,2共三个cha nnel,第二幅图像只有0一个channel,那么输入就一共有4个channes,如果int channels [3] = {3, 2, 0},那么就表示是使用第二副图像的第一个通道和第一副图像的第2和第0个通 道来计算直方图。 mask:掩码。如果mask不为空,那么它必须是一个8位(CV_8U)的数组,并且它的大小的和arrays[i] 的大小相同,值为1的点将用来计算直方图。 hist:计算出来的直方图 dims:计算出来的直方图的维数。 histSize:在每一维上直方图的个数。简单把直方图看作一个一个的竖条的话,就是每一维上竖条的个 数。 ranges:用来进行统计的范围。比如 float rang1[] = {0, 20}; float rang2[] = {30, 40}; const float *rangs[] = {rang1, rang2};那么就是对0,20和30,40范围的值进行统计。 uniform:每一个竖条的宽度是否相等。 accumulate:是否累加。如果为true,在下次计算的时候不会首先清空hist。 |
2.2.2使用Mat::ones创建Mat 示例中使用Mat histImage = Mat::ones(200, 320, CV_8U)*255;创建了一个Mat, 这个Mat的rows为200,cols为320,type为CV_8U(每一位使用一个字节); 并且为其每一个位置赋值为255; 这里就引入了一个重要的函数Mat::ones。
Mat::ones 返回指定大小和类型的的矩阵,并且所有元素值为1。(Matlab中常常使用) 函数原型:MatExpr ones(int rows, int cols, int type);参数说明:rows:行数 cols:列数 type:类型 PS:常以类似Mat A = Mat::ones(100, 100, CV_8U)*3;的形式使用,直接将其元素赋值。 |
2.2.3使用normalize函数归一化
normalize 该函数归一化输入数组使它的范数或者数值范围在一定的范围内。 函数原型:void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());参数说明:src:输入数组 dst :输出数组,支持原地运算 alpha:range normalization模式的最小值 beta:range normalization模式的最大值,不用于norm normalization(范数归一化)模式 normType:归一化的类型,可以有以下的取值: NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化, 一般较常用。 NORM_INF: 此类型的定义没有查到,根据OpenCV 1的对应项,可能是归一化 数组的C-范数(绝对值的最大值) NORM_L1 : 归一化数组的L1-范数(绝对值的和) NORM_L2: 归一化数组的(欧几里德)L2-范数 dtype:dtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数 组只是通道数相同,而tpye=CV_MAT_DEPTH(dtype). mask:操作掩膜,用于指示函数是否仅仅对指定的元素进行操作。 |
2.2.3使用常数为Mat赋值 源码中有一段代码histImage = Scalar::all(255);其实是多余的,将其注释掉也完全没有影响,因为
这段代码意味着使用255为Mat histImage所有元素赋值。 不过这也引入了一个很有意思的地方,Mat类型可以直接通过“=”用常熟为其所有元素赋值。 这很方便:)
2.2.4使用cvRound四舍五入
函数 cvRound, cvFloor, cvCeil 用一种舍入方法将输入浮点数转换成整数。 cvRound 返回和参数最接近的整数值。 cvFloor 返回不大于参数的最大整数值。cvCeil 返回不小于参数的最小整数值。在某些体系结构中该函数 工作起来比标准 C 操作起来还要快。
Example代码
#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int _brightness = 100;
int _contrast = 100;
Mat image;
/* brightness/contrast callback function */
static void updateBrightnessContrast( int /*arg*/, void* )
{
int histSize = 64;
int brightness = _brightness - 100;
int contrast = _contrast - 100;
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
*/
double a, b;
if( contrast > 0 )
{
double delta = 127.*contrast/100;
a = 255./(255. - delta*2);
b = a*(brightness - delta);
}
else
{
double delta = -128.*contrast/100;
a = (256.-delta*2)/255.;
b = a*brightness + delta;
}
Mat dst, hist;
image.convertTo(dst, CV_8U, a, b);
imshow("image", dst);
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, 0);
Mat histImage = Mat::ones(200, 320, CV_8U)*255;
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, CV_32F);
histImage = Scalar::all(255);
int binW = cvRound((double)histImage.cols/histSize);
for( int i = 0; i < histSize; i++ )
rectangle( histImage, Point(i*binW, histImage.rows),
Point((i+1)*binW, histImage.rows - cvRound(hist.at<float>(i))),
Scalar::all(0), -1, 8, 0 );
imshow("histogram", histImage);
}
static void help()
{
std::cout << "\nThis program demonstrates the use of calcHist() -- histogram creation.\n"
<< "Usage: \n" << "demhist [image_name -- Defaults to ../data/baboon.jpg]" << std::endl;
}
const char* keys =
{
"{@image|../data/baboon.jpg|input image file}"
};
int main( int argc, const char** argv )
{
help();
CommandLineParser parser(argc, argv, keys);
string inputImage = parser.get<string>(0);
// Load the source image. HighGUI use.
image = imread( inputImage, 0 );
if(image.empty())
{
std::cerr << "Cannot read image file: " << inputImage << std::endl;
return -1;
}
namedWindow("image", 0);
namedWindow("histogram", 0);
createTrackbar("brightness", "image", &_brightness, 200, updateBrightnessContrast);
createTrackbar("contrast", "image", &_contrast, 200, updateBrightnessContrast);
updateBrightnessContrast(0, 0);
waitKey();
return 0;
}
参考资料1.《学习OpenCV范例(五)——改变图像的对比度和亮度》2.《Opencv改变图像亮度和对比度以及优化》3.《OpenCV2中矩阵的归一化 normalize函数详解》4.《cvRound, cvFloor, cvCeil》5.《为程序界面添加滑动条》