Image color invert on button click C# - c#

I'm using WinForms. I got a picturebox which contains an image. On button click I want to invert the colors of this image back and forth. I tried to make the images invert faster by a button click with the code below. The problem I'm having is that I get an error
Error: Where the code says: Parallel.ForEach(rect.SubRectangles <- I get an error: 'Rectangle' does not contain a definition fo subRectangles' and no extension method 'SubRectangles' accepting a first argument of type 'Rectangel' could be found
2nd error: Under the IEnumerable<Rectangle>SubRectangles <--- (Extension)IEnumerable<Rectangle>ImageUtility.SubRectangle(this Rectanger,... Extension methods must be defined in a top level static class; ImageUtility is a nested class.
Another issue I'm having is calling this code into the button click event so it could work. I'm still learning how to program.
public static class ImageUtility
{
public static Bitmap Process(Bitmap bmp)
{
Bitmap retBmp = new Bitmap(bmp);
var depth = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
if (depth < 3) throw new ArgumentException("Image must be at least 24 bpp.");
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
var data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
var buffer = new byte[data.Width * data.Height * depth];
//copy pixels to buffer
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
bmp.UnlockBits(data);
//Process Image
Parallel.ForEach(rect.SubRectangles(2, 4), r => InvertColor(buffer, r, data.Width, depth));
//Copy the buffer back to image
data = retBmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);
retBmp.UnlockBits(data);
return retBmp;
}
static void InvertColor(byte[] buffer, Rectangle r, int orgWidth, int depth)
{
for (int i = r.X; i < r.X + r.Width; i++)
{
for (int j = r.Y; j < r.Y + r.Height; j++)
{
var offset = ((j * orgWidth) + i) * depth;
buffer[offset + 0] = (byte)(255 - buffer[offset + 0]);
buffer[offset + 1] = (byte)(255 - buffer[offset + 1]);
buffer[offset + 2] = (byte)(255 - buffer[offset + 2]);
}
}
}
public static IEnumerable<Rectangle> SubRectangles(this Rectangle r, int dx, int dy)
{
int incX = (r.Width - r.X) / dx;
int incY = (r.Height - r.Y) / dy;
for (int y = r.Y; y < r.Height; y += incY)
{
for (int x = r.X; x < r.Width; x += incX)
{
yield return new Rectangle(x, y, incX, incY);
}
}
}
}
private void button_Color_Invert_Click(object sender, EventArgs e)
{
//TO DO:
}

Well, it compiles for me. Is your class ImageUtility nested in another class? It shouldn't be, because extension methods must be placed in a top level static class. That's what your second error message is saying. The method SubRectangles is an extension method, because of the this keyword in the parameter list.
class ImageUtility should be placed in its own file.
public static class A // Top level and static
{
class B // Nested
{
}
}
See:
- Static Classes and Static Class Members (C# Programming Guide)
- Extension Methods (C# Programming Guide)

Related

How to properly divide a task of editing bitmap for parallel processing for filter

I'm currently in the development phase of a photoconverter program and in the process of developing a blur filter. At the initial stages of prototyping this feature, i devised a algorithm in which I had an accumulator for each color channel and add all the pixels in a radius of the target pixel. Afterwards the program would divide the accum by the amount of pixels read(not counting those offscreen). At first I thought this would be fine but when it started to work, I had the problem of this filter taking an hour to render with this being the result at the lowest setting. So I opted to utilize parallel processing in C# to make this process much easier and faster to run. With the boost of speed came the cost of the image becoming very glitched out. Here's the image before, and Here's the image afterwards
This is the code I wrote for the filter
public static DirectBitmap NewBlur (DirectBitmap image, int radius)
{
int sectorDiam = 128;
DirectBitmap newimage = image;
List<Rectangle> renderSectors = new List<Rectangle>();
Rectangle rect;
for (int x = 0; x < (image.Width / sectorDiam); x++)
{
int xwidth = sectorDiam;
for (int y = 0; y < (image.Height / sectorDiam); y++)
{
int yheight = sectorDiam;
rect = new Rectangle(x * sectorDiam, y * sectorDiam, xwidth, yheight);
renderSectors.Add(rect);
}
}
var Picrect = new Rectangle(0, 0, image.Width, image.Height);
var data = image.Bitmap.LockBits(Picrect, ImageLockMode.ReadWrite, image.Bitmap.PixelFormat);
var depth = Bitmap.GetPixelFormatSize(data.PixelFormat) / 8; //bytes per pixel
var buffer = new byte[data.Width * data.Height * depth];
Marshal.Copy(data.Scan0, buffer, 0, buffer.Length);
Parallel.ForEach(renderSectors, sector =>
{
BlurSection(buffer, sector, Picrect, radius, image.Width, image.Height, depth);
}
);
Marshal.Copy(buffer, 0, data.Scan0, buffer.Length);
image.Bitmap.UnlockBits(data);
return image;
}
And here's the method for each section of the image to be blurred.
public static void BlurSection(byte[] buffer, Rectangle blurSector, Rectangle bitmaprect, int radius, int width, int height, int depth)
{
int[] Accum = new int[4];
for (int x = blurSector.X; x < blurSector.Width+ blurSector.X; x++)
{
for (int y = blurSector.Y; y < blurSector.Height + blurSector.Y; y++)
{
Accum[0] = 0;
Accum[1] = 0;
Accum[2] = 0;
Accum[3] = 0;
for (int i = -radius; i <= radius; i++)
{
for (int j = -radius; j <= radius; j++)
{
var offset = 0;
offset = (((y+j) * width) + (x+i)) * depth;
if (bitmaprect.Contains(new Point(x + i, y + j))){
Accum[0] += buffer[offset + 0];
Accum[1] += buffer[offset + 1];
Accum[2] += buffer[offset + 2];
Accum[3]++;
}
}
}
Accum[0] = Accum[0] / Accum[3];
if (Accum[0] > 255)
{
Accum[0] = 255;
}
Accum[1] = Accum[1] / Accum[3];
if (Accum[1] > 255)
{
Accum[1] = 255;
}
Accum[2] = Accum[2] / Accum[3];
if (Accum[2] > 255)
{
Accum[2] = 255;
}
var newoffset = ((y * width) + (x * depth*2));
buffer[newoffset + 0] = (byte)Accum[0];
buffer[newoffset + 1] = (byte)Accum[1];
buffer[newoffset + 2] = (byte)Accum[2];
}
}
}
It's also worth noting that I'm using a Bitmap class to make access to pixel data much easier, the "DirectBitmap" you can find here: https://stackoverflow.com/a/34801225/15473435. Is there anything that I'm missing or not aware of that's causing this algorithm not to function?

Synchronization within nested Parallel.For loops

I wanted to turn a regular for loop into a Parallel.For loop.
This-
for (int i = 0; i < bitmapImage.Width; i++)
{
for (int x = 0; x < bitmapImage.Height; x++)
{
System.Drawing.Color oc = bitmapImage.GetPixel(i, x);
int gray = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
System.Drawing.Color nc = System.Drawing.Color.FromArgb(oc.A, gray, gray, gray);
bitmapImage.SetPixel(i, x, nc);
}
}
Into this-
Parallel.For(0, bitmapImage.Width - 1, i =>
{
Parallel.For(0, bitmapImage.Height - 1, x =>
{
System.Drawing.Color oc = bitmapImage.GetPixel(i, x);
int gray = (int)((oc.R * 0.3) + (oc.G * 0.59) + (oc.B * 0.11));
System.Drawing.Color nc = System.Drawing.Color.FromArgb(oc.A, gray, gray, gray);
bitmapImage.SetPixel(i, x, nc);
});
});
It fails with message-
Object is currently in use elsewhere.
at below line since multiple threads trying to access the non-thread safe reasources. Any idea how I can make this work?
System.Drawing.Color oc = bitmapImage.GetPixel(i, x);
It's not a clean solution, seeing what you would like to achieve. It would be better to get all the pixels in one shot, and then process them in the parallel for.
An alternative that I personally used, and improved the performance dramatically, is doing this conversion using unsafe functions to output a grayscale image.
public static byte[] MakeGrayScaleRev(byte[] source, ref Bitmap bmp,int Hei,int Wid)
{
int bytesPerPixel = 4;
byte[] bytesBig = new byte[Wid * Hei]; //create array to contain bitmap data with padding
unsafe
{
int ic = 0, oc = 0, x = 0;
//Convert the pixel to it's luminance using the formula:
// L = .299*R + .587*G + .114*B
//Note that ic is the input column and oc is the output column
for (int ind = 0, i = 0; ind < 4 * Hei * Wid; ind += 4, i++)
{
int g = (int)
((source[ind] / 255.0f) *
(0.301f * source[ind + 1] +
0.587f * source[ind + 2] +
0.114f * source[ind + 3]));
bytesBig[i] = (byte)g;
}
}
try
{
bmp = new Bitmap(Wid, Hei, PixelFormat.Format8bppIndexed);
bmp.Palette = GetGrayScalePalette();
Rectangle dimension = new Rectangle(0, 0, Wid, Hei);
BitmapData picData = bmp.LockBits(dimension, ImageLockMode.ReadWrite, bmp.PixelFormat);
IntPtr pixelStartAddress = picData.Scan0;
Marshal.Copy(forpictures, 0, pixelStartAddress, forpictures.Length);
bmp.UnlockBits(picData);
return bytesBig;
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
return null;
}
}
It gets the bytearray of all the pixels of the input image, its height and width and output the computed grayscale array, and in ref Bitmap bmp the output grayscale bitmap.

libtiff.net writeScanLine returns false

I'm using libtiff.net to make a tiff from a jpeg.
The problem apears when I try to write, because the tiff.writeScanLine returns false, meaning the image isn't written in the tiff.
Why is this happening? And how can I figure out what's wrong?
Here's the code:
private bool creatingTiff()
{
using (Bitmap bmp = new Bitmap(targetFile))
{
using (Tiff tif = Tiff.Open("BitmapTo24BitColorTiff.tif", "w"))
{
byte[] raster = getImageRasterBytes(bmp, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
tif.SetField(TiffTag.IMAGEWIDTH, bmp.Width);
tif.SetField(TiffTag.IMAGELENGTH, bmp.Height);
tif.SetField(TiffTag.COMPRESSION, Compression.OJPEG);
tif.SetField(TiffTag.PHOTOMETRIC, Photometric.YCBCR);
tif.SetField(TiffTag.SUBFILETYPE, 0);
tif.SetField(TiffTag.ROWSPERSTRIP, bmp.Height);
tif.SetField(TiffTag.ORIENTATION, BitMiracle.LibTiff.Classic.Orientation.TOPLEFT);
tif.SetField(TiffTag.XRESOLUTION, bmp.HorizontalResolution);
tif.SetField(TiffTag.YRESOLUTION, bmp.VerticalResolution);
tif.SetField(TiffTag.RESOLUTIONUNIT, ResUnit.INCH);
tif.SetField(TiffTag.BITSPERSAMPLE, 8);
tif.SetField(TiffTag.SAMPLESPERPIXEL, 3);
tif.SetField(TiffTag.JPEGIFOFFSET, 768);
tif.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG);
for (int i = 0, offset = 0; i < bmp.Height; i++)
{
bool b = tif.WriteScanline(raster, offset, i, 0);
Console.WriteLine("write succes: " + b);
offset += stride;
}
}
System.Diagnostics.Process.Start("BitmapTo24BitColorTiff.tif");
return true;
}
}
private static byte[] getImageRasterBytes(Bitmap bmp, System.Drawing.Imaging.PixelFormat format)
{
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
byte[] bits = null;
try
{
// Lock the managed memory
BitmapData bmpdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, format);
// Declare an array to hold the bytes of the bitmap.
bits = new byte[bmpdata.Stride * bmpdata.Height];
// Copy the values into the array.
System.Runtime.InteropServices.Marshal.Copy(bmpdata.Scan0, bits, 0, bits.Length);
// Release managed memory
bmp.UnlockBits(bmpdata);
}
catch
{
return null;
}
return bits;
}
private static void convertSamples(byte[] data, int width, int height)
{
int stride = data.Length / height;
const int samplesPerPixel = 3;
for (int y = 0; y < height; y++)
{
int offset = stride * y;
int strideEnd = offset + width * samplesPerPixel;
for (int i = offset; i < strideEnd; i += samplesPerPixel)
{
byte temp = data[i + 2];
data[i + 2] = data[i];
data[i] = temp;
}
}
}
The tif-tags are written, but the image itself isn't. Perhaps if someone can point me in the direction of the library developers blog (BitMiracle), I can direct my problem to them directly.
I think your code has the following errors:
You can not use Compression.OJPEG for new images. Old-JPEGs can only be de-compressed.
You probably should not specify TiffTag.JPEGIFOFFSET value by hand. The library will specify proper value itself.
You are trying to write the whole strip using WriteScanline method. You should use WriteEncodedStrip instead.
It also helps to review warnings emitted by the library (it emits them into console).

c# image filter parallel.for takes longer time [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
here is the code is there any way to make it faster cause its slowerden single
public Bitmap pSetInvert(Bitmap _currentBitmap)
{
Bitmap temp = (Bitmap)_currentBitmap;
Bitmap bmap = (Bitmap)temp.Clone();
Color c;
Parallel.For(0, bmap.Width, i =>
{
lock (bmap)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
bmap.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));
}
}
});
return (Bitmap)bmap.Clone();
}
By using lock you are making parallel part of your code to work serially, exactly like a single thread application with synchronization overhead so it actually would be slower.
here is a helper class that access bitmap data directly and you can manipulate image in concurrently.
FastImage Helper Class:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
namespace ImageManipulation
{
class FastImage : IDisposable
{
private Bitmap _buffer;
private byte[] _rawData;
private GCHandle _rawHandle;
private int _formatSize;
private int _width;
private int _height;
public int Width
{
get { return _width; }
}
public int Height
{
get { return _height; }
}
public byte[] GetRawData()
{
return _rawData;
}
public byte this[int index]
{
get { return _rawData[index]; }
set { _rawData[index] = value; }
}
public Color this[int x, int y]
{
get
{
return GetPixel(x, y);
}
set
{
SetPixel(x, y, value);
}
}
public Color GetPixel(int x, int y)
{
var offset = y*_width*_formatSize;
offset += x*_formatSize;
return Color.FromArgb(_rawData[offset + 3], _rawData[offset + 2], _rawData[offset + 1], _rawData[offset]);
}
public void SetPixel(int x, int y, Color value)
{
var offset = y*_width*_formatSize;
offset += x*_formatSize;
_rawData[offset] = value.B;
_rawData[offset + 1] = value.G;
_rawData[offset + 2] = value.R;
_rawData[offset + 3] = value.A;
}
private FastImage() { }
public static FastImage Create(Image source)
{
var image = new FastImage();
var bmpSource = new Bitmap(source);
var bmpData = bmpSource.LockBits(new Rectangle(0, 0, source.Width, source.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpSource.PixelFormat);
image._width = source.Width;
image._height = source.Height;
image._formatSize = 4;
var stride = bmpSource.Width * image._formatSize;
image._rawData = new byte[stride * bmpSource.Height];
image._rawHandle = GCHandle.Alloc(image._rawData, GCHandleType.Pinned);
var pointer = Marshal.UnsafeAddrOfPinnedArrayElement(image._rawData, 0);
image._buffer = new Bitmap(bmpSource.Width, bmpSource.Height, stride, PixelFormat.Format32bppArgb /*bmpSource.PixelFormat*/, pointer);
bmpSource.UnlockBits(bmpData);
var graphics = Graphics.FromImage(image._buffer);
graphics.DrawImageUnscaledAndClipped(bmpSource, new Rectangle(0, 0, source.Width, source.Height));
graphics.Dispose();
return image;
}
public void Dispose()
{
_rawHandle.Free();
_buffer.Dispose();
}
public void Save(Stream stream)
{
_buffer.Save(stream, ImageFormat.Bmp);
}
public Bitmap ToBitmap()
{
return (Bitmap)_buffer.Clone();
}
}
}
and here is your code using FastImage class:
public Bitmap pSetInvert(Bitmap _currentBitmap)
{
using (var bmap = FastImage.Create(_currentBitmap))
{
Parallel.For(0, bmap.Width, i =>
{
for (int j = 0; j < bmap.Height; j++)
{
var c = bmap.GetPixel(i, j);
bmap.SetPixel(i, j, Color.FromArgb(255 - c.R, 255 - c.G, 255 - c.B));
}
});
return bmap.ToBitmap();
}
}
The lock in the Parallel.For is going to cause the code to run slower than with a single-threaded loop. The lock only allows one thread at a time to do useful work, with the added cost of acquiring the lock.
Additionally, GetPixel and SetPixel are extremely slow. They are also not guaranteed to be thread-safe, which is probably why you are getting the InvalidOperationException
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
http://msdn.microsoft.com/en-us/library/System.Drawing.Bitmap(v=vs.110).aspx
Have a look instead at WriteableBitmap. Though the class was introduced with WPF, you can use it from a variety of environments. I recently used it in a console application. WriteableBitmap can be converted to a standard bitmap if needed, or written to a ".bmp" file.
Alternatively, you can use unsafe code to directly access the Bitmap buffer.
If you need to, you can use multiple threads for either WriteableBitmap or unsafe access to the Bitmap buffer since you are directly reading/writing memory.
Here are two versions of your filter routine you can play with. They take the image of one PictureBox and after running through the filter assign it to a second PictureBox.
The first one uses LockBits and can do 10.000 (!) loops for a ca 400x400 image in 40 seconds on my machine.
The second uses Parallel.For in addition to the LockBits and does the same job in 35 seconds here.
Of course these timings are strongly influenced by overhead (and maybe by dumb bugs I made.) But really, using Lockbits is key to speed here as there is so little to 'compute' the parallelization management itself eats up most of the cores' time..
using System.Runtime.InteropServices;
// ..
public void filter1()
{
if (pictureBox2.Image != null) pictureBox2.Image.Dispose();
Bitmap bmp = new Bitmap(pictureBox1.Image);
Size s1 = bmp.Size;
PixelFormat fmt1 = bmp.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, fmt1);
byte bpp = 4; // <<-------------------set to 3 for 24bbp !!
int size1 = bmpData.Stride * bmpData.Height;
byte[] data = new byte[size1];
Marshal.Copy(bmpData.Scan0, data, 0, size1);
for (int y = 0; y < s1.Height; y++)
{
for (int x = 0; x < s1.Width; x++)
{
int index = y * bmpData.Stride + x * bpp;
data[index + 0] = (byte) (255 - data[index + 0]); // Blue
data[index + 1] = (byte) (255 - data[index + 1]); // Green
data[index + 2] = (byte) (255 - data[index + 2]); // Red
data[index + 3] = 255; // Alpha, comment out for 24 bpp!
}
}
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
pictureBox2.Image = bmp;
}
public void filter2()
{
if (pictureBox2.Image != null) pictureBox2.Image.Dispose();
Bitmap bmp1 = new Bitmap(pictureBox1.Image);
Size s1 = bmp1.Size;
Bitmap bmp2 = new Bitmap(s1.Width, s1.Height);
PixelFormat fmt1 = bmp1.PixelFormat;
Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
BitmapData bmpData1 = bmp1.LockBits(rect, ImageLockMode.ReadOnly, fmt1);
BitmapData bmpData2 = bmp2.LockBits(rect, ImageLockMode.WriteOnly, fmt1);
byte bpp = 4; // set to 3 for 24bbp !!
int size1 = bmpData1.Stride * bmpData1.Height;
byte[] data1 = new byte[size1];
byte[] data2 = new byte[size1];
Marshal.Copy(bmpData1.Scan0, data1, 0, size1);
Marshal.Copy(bmpData2.Scan0, data2, 0, size1);
int degreeOfParallelism = Environment.ProcessorCount - 1;
var options = new ParallelOptions();
options.MaxDegreeOfParallelism = degreeOfParallelism;
Parallel.For(0, bmp1.Width, options, y =>
{
{
for (int x = 0; x < s1.Width; x++)
{
int index = y * bmpData1.Stride + x * bpp;
data2[index + 0] = (byte)(255 - data1[index + 0]); // Blue
data2[index + 1] = (byte)(255 - data1[index + 1]); // Green
data2[index + 2] = (byte)(255 - data1[index + 2]); // Red
data2[index + 3] = 255; // Alpha, comment out for 24 bpp!
}
}
});
Marshal.Copy(data2, 0, bmpData2.Scan0, data2.Length);
bmp1.UnlockBits(bmpData1);
bmp2.UnlockBits(bmpData2);
pictureBox2.Image = bmp2;
}
Note that the 2nd filter routine needs to work on a second Bitmap for general purpose filter algorithms. This simple inversion filter doesn't really need it, but once you write things that access neighbouring pixels like eg.g a blur filter you do..
Also note the order of the channel bytes!

unsafe image noise removal in c# (error : Bitmap region is already locked)

public unsafe Bitmap MedianFilter(Bitmap Img)
{
int Size =2;
List<byte> R = new List<byte>();
List<byte> G = new List<byte>();
List<byte> B = new List<byte>();
int ApetureMin = -(Size / 2);
int ApetureMax = (Size / 2);
BitmapData imageData = Img.LockBits(new Rectangle(0, 0, Img.Width, Img.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
byte* start = (byte*)imageData.Scan0.ToPointer ();
for (int x = 0; x < imageData.Width; x++)
{
for (int y = 0; y < imageData.Height; y++)
{
for (int x1 = ApetureMin; x1 < ApetureMax; x1++)
{
int valx = x + x1;
if (valx >= 0 && valx < imageData.Width)
{
for (int y1 = ApetureMin; y1 < ApetureMax; y1++)
{
int valy = y + y1;
if (valy >= 0 && valy < imageData.Height)
{
Color tempColor = Img.GetPixel(valx, valy);// error come from here
R.Add(tempColor.R);
G.Add(tempColor.G);
B.Add(tempColor.B);
}
}
}
}
}
}
R.Sort();
G.Sort();
B.Sort();
Img.UnlockBits(imageData);
return Img;
}
I tried to do this. but i got an error call "Bitmap region is already locked" can anyone help how to solve this. (error position is highlighted)
GetPixel is the slooow way to access the image and doesn't work (as you noticed) anymore if someone else starts messing with the image buffer directly. Why would you want to do that?
Check Using the LockBits method to access image data for some good insight into fast image manipulation.
In this case, use something like this instead:
int pixelSize = 4 /* Check below or the site I linked to and make sure this is correct */
byte* color =(byte *)imageData .Scan0+(y*imageData .Stride) + x * pixelSize;
Note that this gives you the first byte for that pixel. Depending on the color format you are looking at (ARGB? RGB? ..) you need to access the following bytes as well. Seems to suite your usecase anyway, since you just care about byte values, not the Color value.
So, after having some spare minutes, this is what I'd came up with (please take your time to understand and check it, I just made sure it compiles):
public void SomeStuff(Bitmap image)
{
var imageWidth = image.Width;
var imageHeight = image.Height;
var imageData = image.LockBits(new Rectangle(0, 0, imageWidth, imageHeight), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
var imageByteCount = imageData.Stride*imageData.Height;
var imageBuffer = new byte[imageByteCount];
Marshal.Copy(imageData.Scan0, imageBuffer, 0, imageByteCount);
for (int x = 0; x < imageWidth; x++)
{
for (int y = 0; y < imageHeight; y++)
{
var pixelColor = GetPixel(imageBuffer, imageData.Stride, x, y);
// Do your stuff
}
}
}
private static Color GetPixel(byte[] imageBuffer, int imageStride, int x, int y)
{
int pixelBase = y*imageStride + x*3;
byte blue = imageBuffer[pixelBase];
byte green = imageBuffer[pixelBase + 1];
byte red = imageBuffer[pixelBase + 2];
return Color.FromArgb(red, green, blue);
}
This
Relies on the PixelFormat you used in your sample (regarding both the pixelsize/bytes per pixel and the order of the values). If you change the PixelFormat this will break.
Doesn't need the unsafe keyword. I doubt that it makes a lot of difference, but you are free to use the pointer based access instead, the method would be the same.

Categories