C# Bitmap图像处理(含增强对比度的三种方法)

 更新时间:2021年11月18日 16:47  点击:612 作者:莫干

Bitmap类

Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象.该类的主要方法和属性如下:

1. GetPixel方法和SetPixel方法:获取和设置一个图像的指定像素的颜色.
2. PixelFormat属性:返回图像的像素格式.
3. Palette属性:获取和设置图像所使用的颜色调色板.
4. Height Width属性:返回图像的高度和宽度.
5. LockBits方法和UnlockBits方法:分别锁定和解锁系统内存中的位图像素.在基于像素点的图像处理方法中使用LockBits和UnlockBits是一个很好的方式,这两种方法可以使我们指定像素的范围来控制位图的任意一部分,从而消除了通过循环对位图的像素逐个进行处理,每调用LockBits之后都应该调用一次UnlockBits.

BitmapData类

BitmapData对象指定了位图的属性
1. Height属性:被锁定位图的高度.
2. Width属性:被锁定位图的宽度.
3. PixelFormat属性:数据的实际像素格式.
4. Scan0属性:被锁定数组的首字节地址,如果整个图像被锁定,则是图像的第一个字节地址.
5. Stride属性:步幅,也称为扫描宽度.

这里要重点说说Stride属性,这个和Width有什么区别呢,可以这么说,如果你的图片大小也就是图片字节是4的整数倍,那么Stride与Width是相等的,否则Stride就是大于Width的最小4的整数倍。在处理过程中,Stride肯定是4的整数倍,这里是个坑啊。。。

                                                       

例1:有一个一维像素点阵数组,里面放的是每个像素点的灰度值,知道宽和高,要转换成bitmap

/// <summary>
/// 像素点阵转换为bitmap
/// </summary>
/// <param name="rawValues">byte[]数组</param>
/// <param name="width">图片的宽度</param>
/// <param name="height">图片的高度</param>
/// <returns>bitmap图片</returns>
public static Bitmap ToGrayBitmap(byte[] rawValues, int width, int height)
{
    Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
    BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
     获取图像参数  
    //bmpData.Stride = width;
    int stride = bmpData.Stride;  // 扫描线的宽度  
    int offset = stride - width;  // 显示宽度与扫描线宽度的间隙  
    IntPtr iptr = bmpData.Scan0;  // 获取bmpData的内存起始位置  
    int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小  
     下面把原始的显示大小字节数组转换为内存中实际存放的字节数组  
    int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组  
    byte[] pixelValues = new byte[scanBytes];  //为目标数组分配内存  
    for (int x = 0; x < height; x++)
    {
         下面的循环节是模拟行扫描  
        for (int y = 0; y < width; y++)
        {
            pixelValues[posScan++] = rawValues[posReal++];
        }
        posScan += offset;  //行扫描结束,要将目标位置指针移过那段“间隙”  
    }
     用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中  
    System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, iptr, scanBytes);
    bmp.UnlockBits(bmpData);  // 解锁内存区域  
     下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度  
    ColorPalette tempPalette;
    using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))
    {
        tempPalette = tempBmp.Palette;
    }
    for (int i = 0; i < 256; i++)
    {
        tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
    }
 
    bmp.Palette = tempPalette;
 
     算法到此结束,返回结果  
    return bmp;
}

至于24位位图数据其实就是 一个像素点有rgb三个值而已,道理一样。

例2::根据图片得到他的灰度数组

//8位位图得到除去文件头信息的一位灰度数组
 
 
BitmapData bmpData = map.LockBits(new System.Drawing.Rectangle(0, 0, map.Width, map.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
 
 获取图像参数  
 
int stride = bmpData.Stride;  // 扫描线的宽度  
 
int offset = stride - map.Width;  // 显示宽度与扫描线宽度的间隙  
 
IntPtr iptr = bmpData.Scan0;  // 获取bmpData的内存起始位置  
 
int scanBytes = stride * map.Height;// 用stride宽度,表示这是内存区域的大小  
 
 下面把原始的显示大小字节数组转换为内存中实际存放的字节数组  
 
mapdata = new byte[scanBytes];  //为目标数组分配内存
 
System.Runtime.InteropServices.Marshal.Copy(iptr, mapdata, 0, scanBytes); //copy内存中数据到数组中

这里对与bitmapdata的操作方式是ReadOnly

下面的三个例子分别基于像素(GetPixel和SetPixel)、基于内存、基于指针这三种方法增强图片对比度。均测试通过

运行时间:

1)基于像素:400-600ms
2)基于内存:17-18ms
3)基于指针:20-23ms
利用LUT,应该可以进一步减少运行时间

 // 第一种方法:像素提取法。速度慢
        public Bitmap MethodBaseOnPixel(Bitmap bitmap,int degree)
        {
            Color curColor;
            int grayR, grayG, grayB;
 
            double Deg = (100.0 + degree) / 100.0;
            for (int i = 0; i < bitmap.Width; i++)
            {
                for (int j = 0; j < bitmap.Height; j++)
                {
                    curColor = bitmap.GetPixel(i, j);
                    grayR =Convert.ToInt32((((curColor.R / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    grayG = Convert.ToInt32((((curColor.G / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    grayB = Convert.ToInt32((((curColor.B / 255.0 - 0.5) * Deg + 0.5)) * 255);
                    if (grayR < 0)
                        grayR = 0;
                    else if (grayR > 255)
                        grayR = 255;
 
                    if (grayB < 0)
                        grayB = 0;
                    else if (grayB > 255)
                        grayB = 255;
 
                    if (grayG < 0)
                        grayG = 0;
                    else if (grayG > 255)
                        grayG = 255;
 
 
                    bitmap.SetPixel(i, j, Color.FromArgb(grayR, grayG, grayB));
                }
            }
 
            return bitmap;
        }

// 第二种方法:基于内存
        public unsafe Bitmap MethodBaseOnMemory(Bitmap bitmap, int degree)
        {
            if (bitmap == null)
            {
                return null;
            }
            double Deg = (100.0 + degree) / 100.0;
 
            int width = bitmap.Width;
            int height = bitmap.Height;
 
            int length = height * 3 * width;
            byte[] RGB = new byte[length];
 
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
 
            System.IntPtr Scan0 = data.Scan0;
            System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);
 
            double gray = 0;
            for (int i = 0; i < RGB.Length; i += 3)
            {
                for (int j = 0; j < 3; j++)
                {
                    gray = (((RGB[i + j] / 255.0 -0.5) * Deg+0.5)) * 255.0;
                    if (gray > 255)
                        gray = 255;
 
                    if (gray < 0)
                        gray = 0;
                    RGB[i + j] = (byte) gray;
                }
            }
 
            System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);// 此处Copy是之前Copy的逆操作
            bitmap.UnlockBits(data);
            return bitmap;
        }
    }

//第三种方法:基于指针
        public unsafe Bitmap MethodBaseOnPtr(Bitmap b, int degree)
        {
            if (b == null)
            {
                return null;
            }
            try
            {
                double num = 0.0;
                double num2 = (100.0 + degree) / 100.0;
                num2 *= num2;
                int width = b.Width;
                int height = b.Height;
                BitmapData bitmapdata = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                byte* numPtr = (byte*)bitmapdata.Scan0;
 
                int offset = bitmapdata.Stride - (width * 3);
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        for (int k = 0; k < 3; k++)
                        {
                            num = ((((((double)numPtr[k]) / 255.0) - 0.5) * num2) + 0.5) * 255.0;
                            if (num < 0.0)
                            {
                                num = 0.0;
                            }
                            if (num > 255.0)
                            {
                                num = 255.0;
                            }
                            numPtr[k] = (byte)num;
                        }
                        numPtr += 3;
 
                    }
                    numPtr += offset;
                }
                b.UnlockBits(bitmapdata);
                return b;
            }
            catch
            {
                return b;
            }
        }

参考:

1.  http://blog.csdn.net/jiangxinyu/article/details/6222302 (此博客的代码中有错误,精简代码基于内存处理的copy顺序有问题)

2.  http://www.pin5i.com/showtopic-20228.html  // C# 特效图片:雾化、浮雕等。

到此这篇关于C# Bitmap图像处理(含增强对比度的三种方法)的文章就介绍到这了,更多相关C# Bitmap图像处理内容请搜索猪先飞以前的文章或继续浏览下面的相关文章希望大家以后多多支持猪先飞!

原文出处:https://blog.csdn.net/qq_20161893/article/details/78478125

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25