如何在C#中以像素级别操作图像

48

如何在C#中以像素级别操作图像?

我需要能够单独读取/修改每个位图像素的RGB值。

如果有代码示例将不胜感激。

5个回答

51

如果你想要速度,那么使用 LockBits。如果你只需要编辑几个像素,那么GetPixel/SetPixel 可以满足你的需求。


不错。我从来没有去研究过超越设置/获取像素的内容。感谢提供链接。 - mattlant
8
Bob Powell没有告诉你的是,在C#中实际上不需要处理指针。查看MSDN库中的LockBits,其中有一个示例可以将存储器复制到托管数组中,该方法未标记为unsafe,因此应用程序不必设置为unsafe :) - OregonGhost
2
XNA用户们请注意,XBox360的.NET CF不支持System.Drawing库,而这里正是LockBits所在的地方。因此,托管DirectX很可能是你唯一的选择(感谢Ash)。 - Engineer
3
这里链接的网站已经无法访问。 - Tedd Hansen
“here” 链接的网站现在是一个弹出消息框病毒欺诈。 - Beeeaaar

16

以下是一段样例代码(我用它来实现简单的合并和比较功能)。它需要两张图片,并生成第三张灰度图像,显示两个原始图片之间的差异程度。颜色越深,差异越大:

    public static Bitmap Diff(Bitmap src1, Bitmap src2, int x1, int y1, int x2, int y2, int width, int height)
{
    Bitmap diffBM = new Bitmap(width, height, PixelFormat.Format24bppRgb);

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            //Get Both Colours at the pixel point
            Color col1 = src1.GetPixel(x1 + x, y1 + y);
            Color col2 = src2.GetPixel(x2 + x, y2 + y);

            // Get the difference RGB
            int r = 0, g = 0, b = 0;
            r = Math.Abs(col1.R - col2.R);
            g = Math.Abs(col1.G - col2.G);
            b = Math.Abs(col1.B - col2.B);

            // Invert the difference average
            int dif = 255 - ((r+g+b) / 3);

            // Create new grayscale RGB colour
            Color newcol = Color.FromArgb(dif, dif, dif);

            diffBM.SetPixel(x, y, newcol);

        }
    }

    return diffBM;
}

马克的帖子指出了 LockBits 并建议直接在内存中修改图像。如果性能是一个关注点,我建议查看那个帖子。谢谢马克!


可能已经多次提到,GetPixelSetPixel非常慢。 Bitmap.LockBitsMarshal.Copy结合使用可以实现相同的功能,但是以块状数据而不是逐像素获取数据,这使得速度更快。 - Nyerguds

5
System.Drawing.Bitmap有一个公共方法GetPixel(int x, int y),返回一个System.Drawing.Color结构体。该结构体有字节成员R、G、B和A,您可以直接修改它们,然后再次调用SetPixel(Color)在您的Bitmap上。
不幸的是,这将相对较慢,但它是C#中最简单的方法。如果您需要频繁地操作单个像素并发现性能不足,并且需要更快的处理速度,则可以使用LockBits...然而要复杂得多,因为您需要了解该颜色深度和类型的位结构,并处理位图的步幅等问题...所以如果您发现有必要如此,请确保您寻找一个好的教程!在网上有几个值得一读的教程,搜索“C# LockBits”会让您找到数十篇。

3
如果性能很重要,LockBits的另一种替代方案是托管的DirectX。
有关更多信息,请参见早期的Stack Overflow问题在C#中呈现图形
与LockBits一样,您需要使用unsafe关键字/编译器开关,但可以获得高性能的像素级访问。
与使用普通的Bitmap类和PictureBox控件相比,您还可以通过DirectX后备缓冲获得更高性能的屏幕渲染。

3
托管DirectX现已被弃用。 - sourcenouveau

0
下面列出的代码是一个简单的示例,演示如何使用C#编程语言在Microsoft Visual Studio 2022上创建像素图像编辑器。
namespace GreekPicturePixelEditor
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private int X1 = 0;  // picture box 1 mouse click X position
        private int Y1 = 0;  // picture box 1 mouse click y position
        private bool mousePress = false;  // is left mouse pressed in picture box 2
        private Bitmap statueBitmap;
        private SolidBrush brush;
        private Pen pen;
        private Rectangle rec;
        private Color color;
        private const int pSize = 20;  // pixel size

        // Step 1. Create first picture box 1 at least 512x512 pixel
        //         Create second picture box 2 at least 32x32 pixel
        //         Create a 512x512 bitmap image using MS Paint
        //         Load and display statue bitmap on picture box 1
        private void Form1_Load(object sender, EventArgs e)
        {
            statueBitmap = new Bitmap(@"C:\VS2022\Pic\Greek512x512.bmp");
            pictureBox1.Image = statueBitmap;
        }

        // Step 2. Save X1,Y1 mouse coordinate when user click on picture box 1
        private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
        {
            X1 = e.X;    // Save X1, Y1 mouse coordinate when user click on picture box 1
            Y1 = e.Y;
            pictureBox2.Refresh();  // force re-draw picture box 2
        }

        // Step 3. Create a 32x32 pixel grid on picture box 2. When user click on 
        // picture box 1, a pixel grid is created based on the X1,
        // Y1 coordinate of picture box 2.  In other words, 32x32 bitmap image
        // is cloned starting at X1,Y1 position in picture box 1 to picture box 2.
        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            for( int px = 0; px < 31; px++ )
            {
                for( int py = 0; py < 31; py++ )
                {
                    color = ((Bitmap)pictureBox1.Image).GetPixel( X1 + px, Y1 + py );
                    brush = new SolidBrush( color );
                    pen = new Pen( Form1.DefaultBackColor );
                    rec = new Rectangle( px * pSize, py * pSize, pSize, pSize );
                    g.FillRectangle( brush, rec );
                    g.DrawRectangle( pen, rec );
                }
            }
        }

        // Step 4.  Handle mouse events when drawing picture box 2 with a mouse
        private void pictureBox2_MouseUp(object sender, MouseEventArgs e)
        {
            mousePress = false;
        }

        // Step 4.  Handle mouse events when drawing picture box 2 with a mouse
        private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
        {
            if( e.Button == MouseButtons.Left )
            {
                mousePress = true;
                int X2 = e.X / 20;  // Normalized the mouse position X and Y of picture box 2
                int Y2 = e.Y / 20;
                ((Bitmap)pictureBox1.Image).SetPixel( X1 + X2, Y1 + Y2, System.Drawing.Color.Red );
                pictureBox1.Refresh();
                pictureBox2.Refresh();
            }

        }

        // Step 4.  Handle mouse events when drawing picture box 2 with a mouse
        private void pictureBox2_MouseMove(object sender, MouseEventArgs e)
        {
            if( mousePress )
            {
                int X2 = e.X / 20;  // Normalized the mouse position X and Y of picture box 2
                int Y2 = e.Y / 20;
                ((Bitmap)pictureBox1.Image).SetPixel( X1 + X2, Y1 + Y2, System.Drawing.Color.Red );
                pictureBox1.Refresh();
                pictureBox2.Refresh();
            }
        }

        // Step 5. Deallocate memory to the system
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            statueBitmap.Dispose();
            brush.Dispose();
            pen.Dispose() ;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接