一副图像的亮度对比度调节属于图像的灰度线性变换,其公式如下:
y = [x - 127.5 * (1 - B)] * k + 127.5 * (1 + B)
其中:x为调节前的像素值,y为调节后的像素值。B取值[-1,1],调节亮度;k调节对比度,arctan(k)取值[1,89]。
k = tan( (45 + 44 * c) / 180 * pi );其中c取值[-1,1]。通常我们用该值来设置对比度
特别的:
当B=0 时:y = (x - 127.5) * k + 127.5; 这时只调节对比度。
当c=0 时,k = 1:y = x+ 255 * B; 这时只调节亮度。
根据原理可以将增强算法直接实现:
Mat adjust(Mat img, const float brightness, const float contrast)
{
Mat dst = img.clone();
double B = brightness / 255.;
double c = contrast / 255.;
double k = tan((45 + 44 * c) / 180 * M_PI);
double alpha = k;
double beta = 127.5 * (1 + B) - k * 127.5 * (1 - B);
for(int i=0; i<img.rows; i++)
{
for(int j=0; j<img.cols; j++)
{
for(int c=0; c<3; c++)
{
float x =img.at<Vec3b>(i,j)[c];
//dst.at(i,j)[c] = saturate_cast( (x - 127.5 * (1 - B)) * k + 127.5 * (1 + B) );
dst.at<Vec3b>(i,j)[c] = saturate_cast<uchar>(alpha *img.at<Vec3b>(i,j)[c] +beta);
}
}
}
return dst;
}
注:saturate_cast()作用:大于255取255,小于0取0
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
m – 目标矩阵。如果m在运算前没有合适的尺寸或类型,将被重新分配。
rtype – 目标矩阵的类型。因为目标矩阵的通道数与源矩阵一样,所以rtype也可以看做是目标矩阵的位深度。如果rtype为负值,目标矩阵和源矩阵将使用同样的类型。
alpha – 尺度变换因子(可选)。//控制图像对比度
beta – 附加到尺度变换后的值上的偏移量(可选)。//控制图片亮度
函数将源矩阵中的像素值转换为目标类型。最后会使用溢出保护函数 saturate_cast<> ,以避免转换过程中可能出现的溢出。
注意,是先乘后加即:
alpha*img.at<Vec3b>(i,j)[c] +beta
网上部分写成:
alpha*(img.at<Vec3b>(i,j)[c]+beta)
是错的,属于误导。
了解convertTo后,图像增强算法可以将循环省略:
void adjust(const cv::Mat &src, cv::Mat &dst, const float brightness, const float contrast)
{
double B = brightness / 255.;
double c = contrast / 255.;
double k = tan((45 + 44 * c) / 180 * M_PI);
double alpha = k;
double beta = 127.5 * (1 + B) - k * 127.5 * (1 - B);
src.convertTo(dst, -1, alpha, beta);
}
opencv有多种数据类型例如:
S = 有符号整型 U = 无符号整型 F = 浮点型
CV_8U - 8位无符号整数(0…255)
CV_8S - 8位有符号整数(-128…127)
CV_16U - 16位无符号整数(0…65535)
CV_16S - 16位有符号整数(-32768…32767)
CV_32S - 32位有符号整数(-2147483648…2147483647)
有时参数 rtype 不与输入图像的类型一致,例如:
在应用分水岭算法分割图像时,标记图像为32位有符号整型CV_32S变量(以便定义超过255个标签,每个值标记一类物体,如255标记目标,128标记背景,0标记未知等等)构成的矩阵markers,想要将标记图像显示出来必须转换其数据类型。
markers.convertTo(tmp,CV_8U,255,255);
bool cvMatEq(const cv::Mat& data1, const cv::Mat& data2)
{
bool success = true;
if(data1.dims > 2 || data2.dims > 2)
{
if( data1.dims != data2.dims || data1.type() != data2.type() )
{
return false;
}
for(int dim = 0; dim < data1.dims; dim++)
{
if(data1.size[dim] != data2.size[dim]){
return false;
}
}
}
else
{
if(data1.size() != data2.size() || data1.channels() != data2.channels() || data1.type() != data2.type())
{
return false;
}
}
int Elements = data1.total()*data1.elemSize1();
int cnt = 0;
for(cnt = 0; cnt < Elements && success; cnt++)
{
if(data1.data[cnt] != data2.data[cnt])
{
success = false;
}
}
if(success)
{
cout << "same!!" << endl;
}
else
{
cout << "different" << endl;
}
return success;
}