一种基于平均值和平均偏差的图像亮度检测方法

1.基础知识

(1)平均值


(2)平均偏差

平均偏差是数列中各项数值与其算术平均数的离差绝对值的算术平均数。平均偏差是用来测定数列中各项数值对其平均数离势程度的一种尺度。平均偏差可分为简单平均偏差和加权平均偏差。

简单平均偏差
如果原数据未分组,则计算平均偏差的公式为:

加权平均偏差

在分组情况下,平均偏差的计算公式为:


为什么要取离差的绝对值?因离差和为零,离差的平均数不能将离差和除以离差的个数求得,而必须将离差取绝对数来消除正负号。

平均偏差是反映各标志值与算术平均数之间的平均差异。平均偏差越大,表明各标志值与算术平均数的差异程度越大,该算术平均数的代表性就越小;平均偏差越小,表明各标志值与算术平均数的差异程度越小,该算术平均数的代表性就越大。(下面的亮度判断利用这一特点,当平均偏差小于一定阈值时,才说明算数平均数有代表意义,可以进行进一步判断。)

2.参考博客:

https://blog.csdn.net/kklots/article/details/12720359

这篇博客很多转载,但是作者注释的太不详细了,很多人转载,不知道他们是否真的明白了其中的思路。我不想就这样直接用,还是觉得思考清楚了再用比较好。

作者思路:

首先,计算均值,注意此处的均值不是指图像灰度的均值,指的是(图像灰度值-128)的均值。

da = ∑(xi- 128) / N            N = src.rows * src.cols    i是指扫描图像时每个像素点索引

其次,计算平均差,利用灰度直方图获取每个灰度值对应的像素个数,以像素个数为权重,利用加权平均偏差的计算公式得平均偏差。

Ma = ∑|(xi - 128) - da| * Hist[i] / ∑Hist[i]            i是指【0,256)

然后,根据平均差的值进行判断,此处需要给出一个阈值,作者给的阈值是abs(da)

如果 Ma < abs(da),图像可能存在亮度异常,进一步利用da判断偏暗还是偏亮,如果da>0,说明大多数像素值都是大于128,图像偏亮;如果da<0,说明大多数像素值都是小于128,图像偏暗。

我认为此处的阈值没法给一个准确的值,作者取得这个值可能是经过一些测试设定的,这个阈值不是一个定值,可以根据图像的情况变化,有一定的合理性。


3.基于OpenCV的实现

理解了思路,自己写出来也就很容易了。

//参考博客:https://blog.csdn.net/kklots/article/details/12720359

#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void BrightnessDetect(const cv::Mat &src, int &Refer, float &Mean, float &MeanDev);

int main()
{
	cv::Mat src = cv::imread("C:\\Users\\dell\\Desktop\\2图像太亮.jpg", 1);
	if (src.empty())
	{
		cout << "输入图像为空" << endl;
		return -1;
	}

	int Refer = 128;
	float MeanDev = 0.0; 
	float Mean = 0.0;	
	BrightnessDetect(src, Refer, Mean, MeanDev);
	cout << "平均值: " << Mean << endl;
	cout << "平均偏差: " << MeanDev << endl;
	cout << "判断结果: " << endl;

	//通过平均偏差的大小来判断是否异常
	if (MeanDev < abs(Mean))	//平均偏差小于阈值
	{
		if (Mean > 0)		//均值大于参考值(128),说明图像太亮
			cout << "图像过亮!" << endl;
		else if (Mean < 0)	//均值大于参考值(128),说明图像太暗
			cout << "图像过暗!" << endl;
		else
			cout << "图像亮度正常!" << endl;
	}
	else
		cout << "图像亮度正常!" << endl;

	waitKey(0);
	return 0;
}

void BrightnessDetect(const cv::Mat &src, int &Refer, float &Mean, float &MeanDev)
{
	CV_Assert(!src.empty());

	cv::Mat gray;	//转换为灰度图
	if (3 == src.channels())
		cv::cvtColor(src, gray, CV_BGR2GRAY);
	else
		gray = src.clone();

	//计算整幅图像均值,此处利用函数Scalar mean(InputArray src, InputArray mask=noArray())
	cv::Scalar meanGrayS = cv::mean(gray);
	float meanGray = meanGrayS[0];

	//认为Refer(128)为图像亮度正常值,进一步计算出图像的偏移均值(自己取的,不太好表达)
	Mean = meanGray - Refer;

	//计算图像的偏移均值的平均偏差 MD = ∑|x - Mean(x)| / n
	int nRows = gray.rows;
	int nCols = gray.cols;
	int sumTemp = 0;
	for (int j = 0; j < nRows; j++)
	{
		uchar *pGray = gray.ptr<uchar>(j);
		for (int i = 0; i < nCols; i++)
		{
			int diffTemp = pGray[i] - 128;
			int absTemp = abs(diffTemp - Mean);
			sumTemp += absTemp;
		}
	}
	MeanDev = ((float)sumTemp / (nRows * nCols));

}

4.测试:


5.扩展

(1)做项目的时候,我们关注的往往只是图像中的某一部分,而不是整幅图像。有些情况下整幅图像的亮度正常,但是我们关注的那一部分其实有些亮度异常,需要进行亮度校正。因此,有必要实现一下带图像掩码的亮度检测。实现起来也不难,求均值和平均差时都在掩码图像有效的区域(非0区域)内进行,注意N不能再是整幅图像的像素总数了。

//参考博客:https://blog.csdn.net/kklots/article/details/12720359
//自己博客:https://blog.csdn.net/weixin_42142612/article/details/80901580

#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int BrightnessDetWithMask(const cv::Mat &src, cv::Mat &mask, int &Refer, float &Mean, float &MeanDev);

int main()
{
	cv::Mat src = cv::imread("C:\\Users\\dell\\Desktop\\xin2.jpg", 1);
	if (src.empty())
	{
		cout << "输入图像为空" << endl;
		return -1;
	}

	cv::Mat mask = cv::imread("C:\\Users\\dell\\Desktop\\xin2mask.jpg", 0);
		
	int Refer = 128;
	float MeanDev = 0.0; 
	float Mean = 0.0;	
	BrightnessDetWithMask(src, mask, Refer, Mean, MeanDev);

	cout << "平均值: " << Mean << endl;
	cout << "平均偏差: " << MeanDev << endl;
	cout << "判断结果: " << endl;

	//通过平均偏差的大小来判断是否异常,阈值取abs(Mean)
	if (MeanDev < abs(Mean))	//平均偏差小于阈值,说明各标志值与平均数的差异程度越小,该平均数的代表性就越大
	{
		if (Mean > 0)		//均值大于参考值(128),说明图像太亮
			cout << "图像过亮!" << endl;
		else if (Mean < 0)	//均值大于参考值(128),说明图像太暗
			cout << "图像过暗!" << endl;
		else
			cout << "图像亮度正常!" << endl;
	}
	else
		cout << "图像亮度正常!" << endl;
	
	cv::Mat ValidImg;
	src.copyTo(ValidImg, mask);

	waitKey(0);
	return 0;
}

/*
* 函数功能:计算图像有效区域平均值和平均差,可以完成图像有效区域的亮度异常判断
* 输入参数:src		输入图像
		   mask		输入图像掩码
		   Refer	输入图像正常参考值,一般取128
* 输出参数:Mean		输出图像平均值(注意是各像素值减Refer后的平均值)
		   MeanDev	输出图像平均差
* 返回值:  int		1	正常
					-1	输入图像有误
					-2	输入掩码有误
					-3	输入亮度参考值有误
					-4	有效像素点个数为0,计算无意义
* 备注:根据输出参数可以完成异常判断,假设平均差阈值为T
		MeanDev < T	图像可能存在异常:
			Mean > 0	大多数像素值大于参考值,说明图像太亮
			Mean < 0	大多数像素值小于参考值,说明图像太暗
*/
int BrightnessDetWithMask(const cv::Mat &src, cv::Mat &mask,int &Refer, float &Mean, float &MeanDev)
{
	if(src.empty())
		return -1;		//输入图像为空

	if (mask.empty())	//输入掩码为空,说明图像全部有效
		mask = cv::Mat::ones(src.size(), CV_8UC1);
	else
	{
		if (mask.channels() != 1 || mask.size() != src.size())
			return -2;	//输入掩码不为空,但是格式或者尺寸不对
	}

	if (Refer >= 256 || Refer < 0)
		return -3;		//输入参考值有误,范围应该在[0,256)

	cv::Mat gray;	//转换为灰度图
	if (3 == src.channels())
		cv::cvtColor(src, gray, CV_BGR2GRAY);
	else
		gray = src.clone();

	//计算图像有效区域内均值,此处利用函数Scalar mean(InputArray src, InputArray mask=noArray())
	cv::Scalar meanGrayS = cv::mean(gray, mask);
	float meanGray = meanGrayS[0];

	//认为Refer(128)为图像亮度正常值,进一步计算出图像的偏移均值
	Mean = meanGray - Refer;

	//计算图像有效区域的偏移均值的平均偏差 MD = ∑|x - Mean(x)| / n
	int nRows = gray.rows;
	int nCols = gray.cols;
	int sumTemp = 0;
	int ValidNum = 0;	//有效像素点个数
	for (int j = 0; j < nRows; j++)
	{
		uchar *pGray = gray.ptr<uchar>(j);	//灰度图像指针
		uchar *pmask = mask.ptr<uchar>(j);	//掩码图像指针
		for (int i = 0; i < nCols; i++)
		{
			if (pmask[i])	//注意只在掩码图像像素点非0(有效)时进行计算
			{
				int diffTemp = pGray[i] - 128;
				int absTemp = abs(diffTemp - Mean);
				sumTemp += absTemp;

				ValidNum++;	//统计有效像素点个数
			}
		}
	}

	if (0 == ValidNum)
		return -4;	//有效像素点个数为0,计算无意义

	MeanDev = (float)(sumTemp) / (float)(ValidNum);

	return 1;
}


(2)亮度检测的后续,需要对图像进行亮度校正。可以考虑gamma校正,图像偏亮和图像偏暗设置不同的校正参数。

比如,图像太暗,设置gamma  = 1/2.2,使图像整体亮度值变大;图像太亮,设置gamma = 2.2,使图像整体亮度值变小。

当然gamma校正也可以加上一个图像掩码,只在掩码图像的有效区域进行。


备注:遇到一个问题,如果总想着不思考,直接找到现成的答案,这种惰性思维很可怕,入职一年了,自己明显感觉没积累什么,很大程度就是没养成一个好的解决问题的习惯。要有意识的改变这种惰性思维,加油!!


  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 在RGB颜色空间计算图像亮度的缺点主要有两个: 1. RGB颜色空间并不是一种亮度感知的颜色空间。这意味着,对于相同的RGB颜色值,它们可能在人眼中的亮度感知是不同的,因为人眼对于不同颜色的亮度感知不同。因此,用RGB颜色空间计算图像亮度,可能会得到不准确的结果。 2. RGB颜色空间受到了光源和白平衡的影响。在不同的光源下,同一个RGB颜色值所代表的实际颜色是不同的。而白平衡的调整也会影响到RGB颜色空间中不同颜色通道的值,进而影响到计算得到的亮度值。因此,用RGB颜色空间计算图像亮度,可能会受到光源和白平衡的影响,导致结果不准确。 ### 回答2: 在RGB颜色空间计算图像亮度的缺点是: 1. 亮度计算的方式不够准确:RGB颜色空间是将颜色分为红、绿、蓝三个通道,亮度计算通常使用线性加权平均,即将红、绿、蓝三个通道的数值加权相加。然而,由于人眼对不同颜色的敏感程度不同,同时红、绿、蓝通道的显示器响应也不同,这种简单的加权计算并不能准确反映人眼感知的亮度。 2. 亮度信息的丢失:亮度计算没有考虑到颜色之间的相互影响。由于色彩之间的互相影响,例如红色与绿色相互叠加可能会得到更亮的结果,导致简单的加权平均亮度计算无法准确表示图像的真实亮度。 3. 对亮度的敏感度不够高:在RGB颜色空间中,红、绿、蓝三个通道的取值范围是0-255,而亮度的取值范围也是0-255。这种线性关系导致亮度信息在较暗或较亮区域的细节损失较多,对于感知到的微小亮度变化无法精确表示。 因此,仅仅使用RGB颜色空间计算图像亮度存在这些缺点。为了更准确地计算图像亮度,可以使用其他颜色空间如YUV、HSL等,这些颜色空间在亮度计算时考虑了人眼感知的特性,能够更好地反映图像亮度变化。 ### 回答3: 在RGB颜色空间计算图像亮度的一个重要缺点是它没有考虑到人眼对不同颜色的敏感程度不同这一特点。由于人眼对绿色的感知更为敏感,对蓝色感知较弱,所以在计算亮度时,简单地对RGB通道进行平均或加权求和,会导致绿色通道对亮度的贡献过大,蓝色通道的贡献过小。 这个缺点在一些特定的场景中会引起问题。例如,在绿色背景下的物体可能在亮度上被过度强调,而蓝色背景下的物体可能在亮度上被忽略掉。这可能导致计算出来的亮度与人眼实际感知的亮度不符,使得图像处理结果出现明显的偏差。 为了解决这个问题,一种常见的方法是将RGB颜色空间转换为YUV颜色空间,其中Y通道表示亮度。在YUV颜色空间中,亮度被更准确地表示,并且考虑到了人眼对不同颜色的感知差异。后续的图像处理可以基于Y通道进行,从而更加准确地计算和处理图像亮度。 然而,即使在YUV颜色空间中,仍然存在其他缺点,例如对于不同显示设备的兼容性问题,以及在转换过程中可能引入的信息丢失。因此,在考虑图像亮度时,需要综合考虑不同颜色空间的特点和应用要求,选择合适的方法来计算和处理亮度,以使得结果更加准确和符合人眼感知。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值