文章目录
- 前言
- 公式讲解
- Unity嵌套循环实现
- 使用convertTo实现
- 亮度和对比度调整:
- 伽马矫正
前言
图片处理中这也是非常常用的功能,下面我们一起来学习一下如何在OpenCVForUnity中修改图像的对比度亮度
图像处理中的常见算子可以将一个或多个输入图像转换为输出图像。这些变换包括点运算符(也称像素变换)和邻域运算符。
像素变换仅取决于相应的输入像素值,例如亮度、对比度和颜色的调整。其中两个常见的点处理运算符是加法和乘法。
公式讲解
一点处理是乘法和加法:
二参数和\bate通常称为增益和篇章参数;有时候这些参数被分别控制对比度和亮度. α>β
可以视作为源像素, g(x)作为输出图像像素.表达式为 f(x)g(x)
j表示第i行和第j列.
Unity嵌套循环实现
下面我们用C#在Unity中实现一下,我们使用Imgcodecs加载完图片后,套用上面的公式使用循环嵌套实现一下,代码如下:
//参数α 和 β
float alpha = 2f;
float beta = 0;
//读取图片
string readPath1 = Application.dataPath + "/OpenCVForUnity/Examples/Resources/timg.jpg";
Mat image = Imgcodecs.imread(Utils.getFilePath(readPath1), Imgcodecs.IMREAD_COLOR);
Mat new_image = new Mat(image.size(), CvType.CV_8UC3);
//三层嵌套结构
for (int rows = 0; rows < image.rows(); rows++)
{
for (int cols = 0; cols < image.cols(); cols++)
{
double[] cur = image.get(rows, cols);
for (int c = 0; c < cur.Length; c++)
{
cur[c] = (alpha * cur[c]) + beta;
}
new_image.put(rows, cols, cur);
}
}
//下面是把两个Mat图片内容展示出来的代码,此处略...
下面看下效果:
我们能看到,在读取图片后有个三层嵌套循环。一、循环 每一行
里的每一列
中的每个通道
进行计算。
如果只用上面的方式,那就太低效了.于是OpenCV为我们内置了 convertTo
方法.
将有效地执行 new_image = a * image + beta
。但是,我们想告诉你如何访问每个像素。
在任何情况下,两种方法都给出相同的结果,但是convertTo是更优化的,并且工作得更快。
使用convertTo实现
//参数α 和 β
float alpha = 2f;
float beta = 0;
//读取图片
string readPath1 = Application.dataPath + "/OpenCVForUnity/Examples/Resources/timg.jpg";
Mat image = Imgcodecs.imread(Utils.getFilePath(readPath1), Imgcodecs.IMREAD_COLOR);
Mat new_image = new Mat(image.size(), CvType.CV_8UC3);
image.convertTo(new_image,-1,alpha,beta);
这个与上面的是等效的,这样代码看起来简洁多了,而且更高效。
亮度和对比度调整:
在图像处理中, 通过增加或减少每个像素的常量值来进行亮度和对比度的调整。如果像素值超过0到255的范围,则会被剪裁到该范围内。
直方图是显示每个颜色级别的像素数量的图表。当添加恒定偏差时,直方图将会向右移动,因为每个像素都添加了固定的偏差。水平参数(alpha)会影响颜色级别的分布。如果alpha小于1,则颜色级别将被压缩,导致对比度减小的图像。
这些直方图是使用Gimp软件中的“亮度 - 对比度”工具获得的。亮度调整工具与偏置参数类似,但似乎对比度调整工具中的alpha增益不同,输出范围似乎以Gimp为中心(即先前直方图的图形显示)。使用偏置调整可以提高图像的亮度,但是会导致轻微的模糊,对比度下降。增加alpha增益可以降低此效果,但是饱和会导致一些明亮区域的细节丢失。因而我们需要使用伽马矫正技术。
伽马矫正
伽马校正可以用于校正图像的亮度。这是通过对输入值和映射的输出值之间进行非线性变换来实现的。
当,原始的暗区域会更亮,直方图将向右移动,而\ gamma> 1则会相反。γ<1γ>1
更正曝光不足的图像
整体亮度得到了改善,但是由于数码饱和的原因,可以看到云层已经非常亮了(即在拍摄时被剪辑掉了)。
以下图像已更正:。γ=0.4
伽马校正比以前的方法更倾向于减少饱和效应,因为映射是非线性的,并且没有像以前的方法那样的数字饱和。
左:直方图alpha,β校正; 中心:原始图像的直方图; 右:伽马校正后的直方图
上图比较了三个图像的直方图,可以看到大多数像素值位于原始图像的直方图的较低部分。
在通过其他方法进行调整后,我们可以看到在255的位置有一个高峰,这是由于数码饱和和右侧的偏移所致。
在使用伽马校正后,直方图向右移动,但是暗区域中的像素比明亮区域中的像素更大偏移(参见伽马曲线图)。
这些都是基本技术,不能完全替代光栅图形编辑器。
以下是实现代码:
// 读取图像
Texture2D inputTexture = (Texture2D)Resources.Load("88");
// 将纹理转换为OpenCV Mat对象
Mat inputMat = new Mat(inputTexture.height, inputTexture.width, CvType.CV_8UC4);
Utils.texture2DToMat(inputTexture, inputMat, true);
// 应用gamma矫正
double gamma = 1.5;
Core.pow(inputMat, gamma, inputMat);
// 调整图像亮度
double alpha = 100.0;
double beta = 50.0;
Core.convertScaleAbs(inputMat, inputMat, alpha, beta);
// 将Mat对象转换回Unity Texture2D对象
Texture2D outputTexture = new Texture2D(inputMat.cols(), inputMat.rows(), TextureFormat.RGBA32, false);
Utils.matToTexture2D(inputMat, outputTexture);
// 在Unity中显示
objA.GetComponent<Renderer>().material.mainTexture = inputTexture;
objB.GetComponent<Renderer>().material.mainTexture = outputTexture;