C#图像处理读取图像像素(内存法)
前言
对图像进行处理与识别的前提,是先读取图像的像素。而每个像素有R、G、B三种颜色组成,然后对图片中大量的0~255之间的RGB灰度值进行深入分析和处理。
在C#中读取图像像素、根据像素数组绘制图像的方法有:
(1)Bitmap的GetPixel/SetPixel;
(2)非托管内存的方式Marshal.Copy;
1. Bitmap的GetPixel、SetPixel
/// <summary> /// author:huangyq1984@qq.com
/// 读取位图的RGB灰度值,按BGR的顺序存放到一维数组中
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static byte[] GetImageRgb_Pixel(Bitmap bitmap)
{
if (bitmap == null)
return null;
//数据长度是像素总数*3
byte[] bytes = new byte[bitmap.Width * bitmap.Height * 3];
int i = 0;
for (int x = 0; x < bitmap.Width; x += 1)
{
for (int y = 0; y < bitmap.Height; y += 1)
{
//获取指定像素的Color
Color c = bitmap.GetPixel(x, y);
//按BGR的顺序存放到数组中
bytes[i] = c.B; i += 1;
bytes[i] = c.G; i += 1;
bytes[i] = c.R; i += 1;
}
}
return bytes;
}
/// <summary>
/// author:huangyq1984@qq.com
/// 将包含RGB灰度数据的一维数组,转换为位图输出
/// </summary>
/// <param name="RGB"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public static Bitmap SetImageRgb_Pixel(byte[] RGB, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height);
//数据长度是像素总数*3
int length = height * 3 * width;
if (RGB.Length != length)
return null;
int i = 0;
for (int x = 0; x < bitmap.Width; x += 1)
{
for (int y = 0; y < bitmap.Height; y += 1)
{
//按BGR的顺序从数组中读取灰度值
int r = RGB[x * bitmap.Height * 3 + y * 3 + 2];
int g = RGB[x * bitmap.Height * 3 + y * 3 + 1];
int b = RGB[x * bitmap.Height * 3 + y * 3];
Color c = Color.FromArgb(r,g,b);
//填充颜色到指定像素
bitmap.SetPixel(x, y, c);
}
}
return bitmap;
}
2. 非托管内存的方式
/// <summary> /// author:huangyq1984@qq.com
/// 读取位图的RGB灰度值,按BGR的顺序存放到一维数组中
/// </summary>
/// <param name="bitmap"></param>
/// <returns></returns>
public static byte[] GetImageRgb_Marshal(Bitmap bitmap)
{
if (bitmap == null)
return null;
int width = bitmap.Width;
int height = bitmap.Height;
//像素总数=长*宽,数据长度则是像素总数*3,3表示RGB的3个灰度值(注意实际位图结构的先后顺序是:BGR)
int length = height * 3 * width;
byte[] RGB = new byte[length];
//将bitmap锁定到系统内存中
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//获取位图中第一个像素的地址指针
System.IntPtr Scan0 = data.Scan0;
//将数据从非托管内存指针复制到托管8位无符号整数数组
System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);
//从内存中解锁bitmap
bitmap.UnlockBits(data);
return RGB;
}
/// <summary>
/// author:huangyq1984@qq.com
/// 将包含RGB灰度数据的一维数组,转换为位图输出
/// </summary>
/// <param name="RGB"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
public static Bitmap SetImageRgb_Marshal(byte[] RGB, int width, int height)
{
Bitmap bitmap = new Bitmap(width, height);
//数据长度是像素总数*3
int length = height * 3 * width;
if (RGB.Length != length)
return null;
//将bitmap锁定到系统内存中
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//获取位图中第一个像素的地址指针
System.IntPtr Scan0 = data.Scan0;
//将数据从byte数组复制到非托管内存指针,此处Copy是之前Copy的逆操作
System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);
//从内存中解锁bitmap
bitmap.UnlockBits(data);
return bitmap;
}
3. 前端调用
分别用以上两种方法,先读取像素,再根据像素数组绘制图像。
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "图片文件(*.jpg,*.gif,*.bmp)|*.jpg;*.gif;*.bmp";
if (dialog.ShowDialog() == DialogResult.OK)
{
string filename = dialog.FileName;
Bitmap b = new Bitmap(filename);
//原图
pictureBox3.Image = Image.FromFile(filename);
//方法二
pictureBox1.Image = ImageUtil.SetImageRgb_Marshal(ImageUtil.GetImageRgb_Marshal(b), b.Width, b.Height);
//方法一
pictureBox2.Image = ImageUtil.SetImageRgb_Pixel(ImageUtil.GetImageRgb_Pixel(b), b.Width, b.Height);
}
}
4. 比较
方法一的代码更直观更容易理解,但在效率方面,方法二要远超方法一。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/hyq106/article/details/127385677