OpenCvSharp学习笔记6--改变图像的对比度和亮度

这篇具有很好参考价值的文章主要介绍了OpenCvSharp学习笔记6--改变图像的对比度和亮度。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目的

访问像素值mat.At<T>(y,x)

用0初始化矩阵Mat.Zeros

饱和操作SaturateCast.ToByte

亮度和对比度调整

g(x)=αf(x)+β

用α(>0)和β一般称作增益(gain)和偏置(bias),分别控制对比度和亮度

把f(x)看成源图像像素,把g(x)看成输出图像像素

g(i,j)=α⋅f(i,j)+β

其中,i和j表示像素位于 第i行 和 第j列(左上角为第0行、第0列)

相关函数

Mat.Zeros:初始一个所有值为0的矩阵

Mat new_Image1 = Mat.Zeros(image.Size(), image.Type());

SaturateCast.ToByte:饱和操作,当<0时,返回0,当>255时,返回255,其它返回原值

return SaturateCast.ToByte(alpha * source + beta);

Mat.ConvertTo: g(i,j)=α⋅f(i,j)+β 转化

image.ConvertTo(new_Image2, image.Type(), alpha, beta);

SaturateCast.ToByte与ConvertTo的差异

使用SaturateCast.ToByte与ConvertTo操作的结果不完全一致。

如计算85*0.7+0.0=59.499999999999993D, SaturateCast.ToByte的结果为59,而ConvertTo的结果为60。

图像示例

α=2.2 and β=80时的效果图

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

α、β参数的意义

官网源图

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

β越大,图像越亮,直方图往右

使用前面的公式g(i,j)=α⋅f(i,j)+β对图像进行亮度调整,当α=1,β=80时

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

对比原图(左)与调整后(右)的直方图,调整亮度后,直方图整体往右压缩。因为饱和操作的结果是,大于255的值设为255(为对比显示效果,255的个数没有显示完全)。(直方图:往左变暗,往右变亮)

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

α<1时,对比度变小

当β相同80,左边α=1,右边α=0.5,对比可知,当α<1时,图像色阶变小,对比度变小。

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

(左边α=1,右边α=0.5)

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

结论:调大β,可以变亮,因为对比度的变小导致有轻微蒙纱。调小α,可有效改善,但会损失原来明亮部分的细节。

伽马校正

伽马校正可用于亮度的非性调整。公式如下:

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

对比不同的像素的输入与输出值(图片来源于OpenCV官网)

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

下图为原图与α=1.3,β=40时的对比

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

结论:图像是调亮了,但天空的细节少了

下图为:原图与gamma=0.4时的对比

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

图像调亮了,细节也在。

下图为,原图(左)、α=1.3,β=40调整(中)及gamma=0.4(右)调整的直方图对比

opencvsharp 对比度,opencv,图像处理,c#,Powered by 金山文档

对起简单的线性对比度和亮度调整,伽马校正效果更优。

源码示例

Mat src;//源图

string winName = "Brightness and contrast adjustments";

int alpha = 100;
int beta = 255;

string gammaWinName = "Gamma correction";

Mat saturateCastHistImg;//SaturateCast后的图像直方图

double maxVal = -1;//源图最多像素点个数

public void Run(ParamBase paramBase) {

    //src = Cv2.ImRead(ImagePath.Lena);
    src = Cv2.ImRead(ImagePath.Underexposed);
    if (src.Empty()) throw new Exception("图像打开有误");
    //原图像的直方图
    GetHistResult(src, out _, out Mat histImg);
    Cv2.ImShow("Hist:Original", histImg);

    Cv2.NamedWindow(winName, WindowFlags.AutoSize);
    Cv2.CreateTrackbar("α=n/100", winName, ref alpha, 500, alphaOnChange);
    Cv2.CreateTrackbar("β=n-255", winName, ref beta, 500, betaOnChange);

    //Cv2.ImShow("Original Image", image);

    Cv2.NamedWindow(gammaWinName, WindowFlags.AutoSize);
    Cv2.CreateTrackbar("γ= n/100", gammaWinName, 200, gammaOnChange);
    Cv2.SetTrackbarPos("γ= n/100", gammaWinName, 100);

    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}
#region 滚动条Bug,多次滚动后,ref value的值与pos不同步
private void alphaOnChange(int pos, IntPtr userdata) {
    alpha = pos;
    OnChange();
}
private void betaOnChange(int pos, IntPtr userdata) {
    beta = pos;
    OnChange();
}
#endregion

private void OnChange() {
    //对比度
    double alphaD = alpha / 100.0D;
    //亮度
    double betaD = beta - 255;
    //初始化所有值都为0的矩阵
    Mat new_Image1 = Mat.Zeros(src.Size(), src.Type());

    //使用公式:g(i,j)=α⋅f(i,j)+β
    //方式一
    for (int row = 0; row < src.Rows; row++) {
        for (int col = 0; col < src.Cols; col++) {
            var val = src.At<Vec3b>(row, col);//访问图像像素
            Vec3b newVal = new Vec3b();
            for (int c = 0; c < src.Channels(); c++) {
                newVal[c] = Adjust(val[c], alphaD, betaD);
            }
            new_Image1.At<Vec3b>(row, col) = newVal;
        }
    }
    //方式二
    Mat new_Image2 = Mat.Zeros(src.Size(), src.Type());
    src.ConvertTo(new_Image2, src.Type(), alphaD, betaD);

    //比较 SaturateCast.ToByte与 ConvertTo的结果是否一样
    using var difMat = new Mat();
    Cv2.Absdiff(new_Image1, new_Image2, difMat);
    if (difMat.Split().Any(z => z.CountNonZero() > 0)) {
        //有差异
        PutText(new_Image2, "Different", new Point(5, 30));
        //注意 85*0.7 =     59.499999999999993    double
    }

    GetHistResult(new_Image1, out _, out saturateCastHistImg);
    var adjust = $"alpha={alphaD.ToString("0.00")},beta={betaD}";
    PutText(saturateCastHistImg, adjust);
    Cv2.ImShow("Hist,SaturateCast", saturateCastHistImg);


    PutText(new_Image1, adjust);
    Cv2.HConcat(src, new_Image1, new_Image1);

    PutText(new_Image2, $"alpha={alphaD.ToString("0.00")},beta={betaD}");
    Cv2.HConcat(src, new_Image2, new_Image2);
    Cv2.ImShow(winName, new_Image1);
    Cv2.ImShow("ConvertTo", new_Image2);
}

/// <summary>
/// 调整对比度和亮度
/// </summary>
/// <param name="source">源值</param>
/// <param name="alpha">对比度</param>
/// <param name="beta">亮度</param>
/// <returns></returns>
private byte Adjust(byte source, double alpha, double beta) {
    //饱和操作,当<0时,返回0,当>255时,返回255,其它返回原值
    return SaturateCast.ToByte(alpha * source + beta);
}

#region 直方图相关
/// <summary>
/// 计算并生成绘制直方图
/// </summary>
/// <param name="src">待统计的图像</param>
/// <param name="hist">直方图结果</param>
/// <param name="histImage">直方图的绘制结果</param>
private void GetHistResult(Mat src, out Mat hist, out Mat histImage) {

    hist = new Mat();
    const int histW = 512;
    const int histH = 400;
    histImage = new Mat(histH, histW, MatType.CV_8UC3, Scalar.All(0));

    int histSize = 256;//直方图数组大小
    var range = new Rangef(0, 256);//统计0至255(=266-1)
    //将图像像素灰度[0,255]共分为histSize个等级统计,
    for (int channel = 0; channel < src.Channels(); channel++) {
        Cv2.CalcHist(images: new[] { src },//待统计的图像
                    channels: new[] { channel },//待统计的通道
                    mask: null,//掩膜
                    hist: hist,//输出的统计结果
                    dims: 1,//直方图维度
                    histSize: new[] { histSize },//将range分为histSize梯度
                    ranges: new[] { range });//待统计通道像素的范围,不在这个范围内的不统计
        DrawHist(histImage, hist, (channel == 0 ? Scalar.Blue : (channel == 1 ? Scalar.Green : Scalar.Red)));
    }
}

/// <summary>
/// 绘制直方图
/// </summary>
/// <param name="histImage">直方图绘制结果</param>
/// <param name="histSize">直方图数组大小</param>
/// <param name="color">线的颜色</param>
private void DrawHist(Mat histImage, Mat hist, Scalar color) {
    var binW = Math.Round((double)histImage.Width / hist.Height);
    if (maxVal > 0) {
        //截断超过源图最大值的像素大数(防止饱和像素过多)
        Cv2.Threshold(hist, hist, maxVal, maxVal, ThresholdTypes.Trunc);
    }
    //归一化
    Cv2.Normalize(hist, hist, 0, histImage.Rows, NormTypes.MinMax, -1);
    for (int i = 1; i < hist.Height; i++) {
        var pt1 = new Point2d(binW * (i - 1), histImage.Height - Math.Round(hist.At<float>(i - 1)));
        var pt2 = new Point2d(binW * (i), histImage.Height - Math.Round(hist.At<float>(i)));
        //OpenCvSharp有Bug?有时会报错
        Cv2.Line(histImage, (Point)pt1, (Point)pt2, color, 1, LineTypes.AntiAlias);
    }
}
#endregion

#region Gamma矫正
private Mat GammaCorrection(Mat src,double gamma) {
    var lookUpTable = new Mat(new Size(1, 256), MatType.CV_8U);
    for (int i = 0; i < 256; i++) {
        lookUpTable.At<byte>(0, i) = SaturateCast.ToByte(Math.Pow(i / 255.0D, gamma) * 255.0D);
    }
    Mat dst = new Mat();
    //查表法,性能优化
    Cv2.LUT(src, lookUpTable, dst);
    return dst;
}

private void gammaOnChange(int pos,IntPtr userdata) {
    double gamma = pos / 100.0D;
    using var dst = GammaCorrection(src, gamma);
    GetHistResult(dst, out _, out Mat histImg);

    var adjust = $"gamma={gamma.ToString("0.00")}";
    PutText(histImg,adjust);
    Cv2.ImShow("Hist,Gamma Correction", histImg);

    PutText(dst, adjust);
    //左右合并
    Cv2.HConcat(src, dst, dst);
    Cv2.ImShow(gammaWinName, dst);
}
#endregion

private void PutText(Mat src, string text) {
    PutText(src, text, new Point(5, 15));
}
private void PutText(Mat src, string text, Point point) {
    Cv2.PutText(src, text, point, HersheyFonts.HersheySimplex, 0.5, Scalar.Red);
}

参考

https://docs.opencv.org/4.7.0/d3/dc1/tutorial_basic_linear_transform.html文章来源地址https://www.toymoban.com/news/detail-774439.html

到了这里,关于OpenCvSharp学习笔记6--改变图像的对比度和亮度的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python实现图像对比度增强

    Python实现图像对比度增强 在数字图像处理中,对比度增强被广泛运用于图像增强、图像去噪、特征提取等领域。本文将用Python实现一种简单的图像对比度增强方法,并提供源代码。 首先,我们需要导入必要的库:numpy和OpenCV。在执行之前确保已安装这两个库。 以下是我们实

    2024年02月07日
    浏览(55)
  • OpenCV进阶 —— 调整图像亮度对比度

    图像变换就是找到一个函数,把原始图像矩阵经过函数处理后,转换为目标图像矩阵。 可以分为两种方式,即像素级别的变换和区域级别的变换 Point operators (pixel transforms) Neighborhood (area-based) operators 像素级别的变换就相当于即变换后的每个像素值都与变换前的同位置的像素值

    2024年02月14日
    浏览(48)
  • 【OpenCV • c++】图像对比度调整 | 图像亮度调整

    🚀 个人简介:CSDN「 博客新星 」TOP 10 , C/C++ 领域新星创作者 💟 作    者: 锡兰_CC ❣️ 📝 专    栏: 【OpenCV • c++】计算机视觉 🌈 若有帮助,还请 关注➕点赞➕收藏 ,不行的话我再努努力💪💪💪

    2024年02月11日
    浏览(42)
  • OpenCV增加图像的亮度及对比度

    案例:输出一张原图,增加该图片的亮度及对比度 基本概念: 亮度:RGB的像素值范围是0~255,我们称从0——255随着像素值的增加图像会越来月亮,反之会越来越暗。所以我们可以通过对图像像素加减来改变图像的亮度。 对比度:其反应的是图像中各像素的差异(层次感、落

    2024年02月12日
    浏览(46)
  • 从图像统计特征看图像内在-均值,方差,对比度,熵

    熵 用于度量图像所具有的信息量,它反映了图像中纹理的紊乱度或复杂程度。熵值越大,说明纹理越复杂;熵值越小,说明纹理越平滑。 均值 灰度均值是对区域内亮度的一个度量,可以用来反应图像的明暗程度。 方差 方差就是数据的分散程度(偏离均值)。 对比度 反映了

    2024年02月16日
    浏览(59)
  • 数字图像处理:亮度对比度-几何变换-噪声处理

    亮度与对比度转换 图像变换可分为以下两种: 点算子:基于像素变换,在这一类图像变换中,仅仅根据输入像素值计算相应的输出像素值 邻域算子:基于图像区域进行变换 两种常用的点算子是用常数对点的像素值进行乘法或加法运算,可以表示为: g ( i , j ) = α ∗ f ( i ,

    2024年02月10日
    浏览(61)
  • Python OpenCV调整图像亮度对比度及RGB色彩

    python通过opencv库调整图像的步骤: 1. 读取图像 直接通过cv2库读取图像,可以读取jpg, png等格式 2. 调整图像亮度及对比度 OpenCV提供 convertScaleAbs 函数来调整对比度和亮度,可以直接调用该函数 如果只调整RGB颜色通道,则可以忽略此步骤 3. 分离出图片的B,R,G颜色通道 使用

    2024年02月03日
    浏览(47)
  • Python图像处理:1.插值、频域变换与对比度增强

    7.图像的插值 (1)原理介绍 下面对比三种插值方法,分别是最近邻插值法、双线性插值法、卷积插值法,三种方法的前提和特点、优缺点、适用场景如下: 最近邻插值(Nearest Neighbor Interpolation) : 前提与特点 :这是最简单的插值方法,不考虑相邻像素的影响,只取最近的像

    2024年03月13日
    浏览(63)
  • 【opencv】示例-demhist.cpp 调整图像的亮度和对比度,并在GUI窗口中实时显示调整后的图像以及其直方图。...

    这段代码是使用C++和OpenCV库编写的图像处理程序,其主要功能是 调整图像的亮度和对比度,并在GUI窗口中实时显示调整后的图像以及其直方图。 用户可以通过界面上的滑动条来动态地调整亮度和对比度参数从而观察到图像即时的变化效果。程序首先读取并显示一个灰度图像

    2024年04月10日
    浏览(49)
  • Opencv C++图像处理:亮度+对比度+饱和度+高光+暖色调+阴影+漫画效果+白平衡+浮雕+羽化+锐化+颗粒感

    更多详细信息请看:OpenCV专栏:翟天保Steven

    2024年02月04日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包