对比度亮度图像增强及convertTo详解

1.对比度亮度增强原理

一副图像的亮度对比度调节属于图像的灰度线性变换,其公式如下:

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; 这时只调节亮度。

2.自定义函数做对比度与亮度增强

根据原理可以将增强算法直接实现:

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<Vec3b>(i,j)[c] = saturate_cast<uchar>(  (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

3.cv::convertTo()在图像增强的使用

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);
}

4.cv::convertTo()目标矩阵类型的转换

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);

5.[附] 验证两个图像相同的脚本:

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;
}

猜你喜欢

转载自blog.csdn.net/weixin_48846514/article/details/126911860