How do I manipulate images at the pixel level in C#?
I need to be able to read/modify each bitmap pixel RGB values separately.
A code sample would be appreciated.
How do I manipulate images at the pixel level in C#?
I need to be able to read/modify each bitmap pixel RGB values separately.
A code sample would be appreciated.
If you want speed, then LockBits. See here for a good walkthrough by Bob Powell. If you just want to edit a few, then GetPixel/SetPixel should do what you want.
A sample code routine (I use this for simple merge and compare functionality. It takes two images and produces a third greyscale image showing the differences between the two images as a greyscale tone level. The darker it is, the more the diff.):
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;
}
Marc's post notes LockBits and using that to modify the image directly in memory. I would suggest looking at that rather than what I have posted if performance is a concern. Thanks Marc!
GetPixel
and SetPixel
are notoriously slow. Bitmap.LockBits
in combination with Marshal.Copy
can achieve the same thing, but gets the data out in bulk rather than pixel per pixel, which makes it loads faster. –
Pileus System.Drawing.Bitmap has a GetPixel(int x, int y) public method that returns a System.Drawing.Color structure. That struct has byte members R, G, B, and A, which you can modify directly, and then call SetPixel(Color) on your Bitmap again.
Unfortunately, that's going to be relatively slow, but it's by the easiest way to do it in C#. If you are working with individual pixels a lot and find the performance is lacking, and you need something faster, you can use LockBits... It's a lot more complicated though, as you need to understand the bit structure for that color depth and type, and work with the bitmap's stride and what not... so if you find it's necessary, make sure you find a good tutorial! There are several out there on the web, Googling "C# LockBits" will get you a half dozen that are worth a read.
If performance is critical, another alternative to LockBits is managed DirectX.
See the earlier Stack Overflow question Rendering graphics in C# for more information.
Like Lockbits you will need to use the unsafe keyword/compiler switch, but you get high performance pixel level access.
You also get higher performance screen rendering via DirectX backbuffering, when compared with using the normal Bitmap class and PictureBox control.
The code listed below contains a bare bone example on how to create a pixel image editor using the C# programming language on 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() ;
}
}
}
© 2022 - 2025 — McMap. All rights reserved.