Generate int[,] array based on Bitmap - c#

I'm currently writing an ASCOM driver which is used to take picture with a SigmaFP camera.
Everything works fine but I'm currently hitting a wall when I have to convert my DNG to a standard image array for ASCOM (https://ascom-standards.org/Help/Developer/html/P_ASCOM_DriverAccess_Camera_ImageArray.htm)
With libraw I convert my DNG file, convert it to a bitmap and save it as a new jpeg file.
But, when I try to conver this jpeg file to an array (using Bitmap and my custom ReadBitmap function) I get this kind of pictures :
output image
Here is the input image
I don't know what I have missed during those steps
Here is my ReadBitmap function :
public int[,] ReadBitmap(Bitmap img)
{
BitmapData data = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat);
IntPtr ptr = data.Scan0;
int bytesCount = Math.Abs(data.Stride) * img.Height;
var result = new int[img.Width, img.Height];
byte[] bytesArray = new byte[bytesCount];
Marshal.Copy(ptr, bytesArray, 0, bytesCount);
img.UnlockBits(data);
var width = img.Width;
var height = img.Height;
Parallel.For(0, width * height, rc =>
{
var b = bytesArray[rc * 3];
var g = bytesArray[rc * 3 + 1];
var r = bytesArray[rc * 3 + 2];
int row = rc / width;
int col = rc - width * row;
//var rowReversed = height - row - 1;
result[col, row] = (int)(0.299 * r + 0.587 * g + 0.114 * b);
}
);
return result;
}
Here is the method used to convert DNG to jpeg (which will be used to get the int array)
IntPtr data = LoadRaw(path);
NativeMethods.libraw_dcraw_process(data);
// extract raw data into allocated memory buffer
var errc = 0;
var ptr = NativeMethods.libraw_dcraw_make_mem_image(data, out errc);
// convert pointer to structure to get image info and raw data
var img = PtrToStructure<libraw_processed_image_t>(ptr);
Console.WriteLine("\nImage type: " + img.type);
Console.WriteLine("Image height: " + img.height);
Console.WriteLine("Image width: " + img.width);
Console.WriteLine("Image colors: " + img.colors);
Console.WriteLine("Image bits: " + img.bits);
Console.WriteLine("Data size: " + img.data_size);
Console.WriteLine("Checksum: " + img.height * img.width * img.colors * (img.bits / 8));
// rqeuired step before accessing the "data" array
Array.Resize(ref img.data, (int)img.data_size);
var adr = ptr + OffsetOf(typeof(libraw_processed_image_t), "data").ToInt32();
Copy(adr, img.data, 0, (int)img.data_size);
// calculate padding for lines and add padding
var num = img.width % 4;
var padding = new byte[num];
var stride = img.width * img.colors * (img.bits / 8);
var line = new byte[stride];
var tmp = new List<byte>();
for (var i = 0; i < img.height; i++)
{
Buffer.BlockCopy(img.data, stride * i, line, 0, stride);
tmp.AddRange(line);
tmp.AddRange(padding);
}
// release memory allocated by [libraw_dcraw_make_mem_image]
NativeMethods.libraw_dcraw_clear_mem(ptr);
// create/save bitmap from mem image/array
var bmp = new Bitmap(img.width, img.height, PixelFormat.Format24bppRgb);
var bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
Copy(tmp.ToArray(), 0, bmd.Scan0, (int)img.data_size);
NativeMethods.libraw_close(data);
var outJPEG = path.Replace(Path.GetExtension(path), ".jpg");
Console.WriteLine("Saving image to: " + outJPEG);
bmp.Save(outJPEG, ImageFormat.Jpeg);
CurrentFilePath = outJPEG;

Bitmaps use a stride to describe the number of bytes of a row, this is to ensure all rows are aligned properly, i.e. stride >= width * bitsPerPixel. Additionally you use a parallel loop over all pixels and this is to fine grained parallelism, you should go parallel over rows, and process each row sequentially.
So your code should look something like
var result = new int[height, width];
Parallel.For(0, height, y =>
{
var rowStart = y * data.Stride;
for(var x = 0 ; x < width; x++){
var i = rowStart + x*3;
var b = bytesArray[i];
var g = bytesArray[i + 1];
var r = bytesArray[i + 2];
result[y, x] = (int) (0.299 * r + 0.587 * g + 0.114 * b);
}
}
);
You might also consider using unsafe code to access the pixelvalues from the datapointer directly, instead of using a large temporary buffer that is allocated on the large object heap and immediately thrown away.
You should probably also do a check of the pixel format to ensure it is bgr24, since your code will fail otherwise.
Node that multidimensional arrays may not be ideal for images. Images are usually indexed like [x,y] and converted to a linear index like var i = y * width + x. For multidimensional arrays the storage order is inverse, this might require indexing like [y,x], or doing unnecessary transpositions of images instead of a straight memory-copy whenever converting data to some other format. So I usually prefer to write my own 2D array since this usually make interoperability much easier:
public class Array2D<T> : IReadOnlyList<T>
{
public int Width { get; }
public int Height { get; }
public T[] Data { get; }
public Array2D(int width, int height)
{
Width = width;
Height = height;
Data = new T[width * height];
}
/// <summary>
/// Index operator
/// Note that this does not check if x is valid, it may wrap around to the next row
/// </summary>
public T this[int x, int y]
{
get => Data[y * Width + x];
set => Data[y * Width + x] = value;
}
}

Related

How to get raw pixel data from Texture2D?

I am trying to get pixel(rgba) values from SharpDX Texture2D and then to assign them to bitmap but i get this wrong image:
I know that the problem is when i read values from srcPtr(See code below), i think the offsets are wrong...
This is the code:
SharpDX.DXGI.Resource screenResource;
OutputDuplicateFrameInformation duplicateFrameInformation;
// Try to get duplicated frame within given time is ms
duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource);
// copy resource into memory that can be accessed by the CPU
using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
device.ImmediateContext.CopyResource(screenTexture2D, screenTexture);
// Get the desktop capture texture
var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None);
// Create Drawing.Bitmap
using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb))
{
var boundsRect = new Rectangle(0, 0, width, height);
// Copy pixels from screen capture Texture to GDI bitmap
var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
unsafe
{
byte* srcPtr = (byte*)mapSource.DataPointer;
byte* destPtr = (byte*)mapDest.Scan0;
var curRowOffs = 0;
var stride = width * 4;
byte[] rgbaValues = new byte[width*height*4];
for (uint y = 0; y < height; y++)
{
var index = curRowOffs;
for (uint x = 0; x < width; x++)
{
// ARGB = bytes [B,G,R,A]
rgbaValues[index] = srcPtr[index];
rgbaValues[index + 1] = srcPtr[index + 1];
rgbaValues[index + 2] = srcPtr[index + 2];
rgbaValues[index + 3] = srcPtr[index + 3];
//Copy rgba to bitmap
destPtr[index + 3] = srcPtr[index + 3];
destPtr[index + 2] = srcPtr[index + 2];
destPtr[index + 1] = srcPtr[index + 1];
destPtr[index] = srcPtr[index];
index += 4;
}
// Increase row offset
curRowOffs += stride;
}
}
// Release source and dest locks
bitmap.UnlockBits(mapDest);
device.ImmediateContext.UnmapSubresource(screenTexture, 0);
What i am doing wrong here?
How to put the raw pixels data into byte array?

Image processing : How to perform this function with lockbits

I have a problem. I need to perform this function with lockbits. Please I need help.
public void xPix(Bitmap bmp, int n, Color cx, Color nx)
{
try
{
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x += (n * 2))
{
cx = bmp.GetPixel(x, y);
if (x + n <= bmp.Width - 1) nx = bmp.GetPixel(x + n, y);
bmp.SetPixel(x, y, nx);
if (x + n <= bmp.Width - 1) bmp.SetPixel(x + n, y, cx);
}
}
}
catch { }
}
There were lots of things that didn't make sense to me about your code. I fixed the pieces that were preventing an image from appearing and here is the result. I will explain my changes after the code.
public void xPix(Bitmap bmp, int n, Color cx, Color nx)
{
var img = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte[] bmpBytes = new byte[Math.Abs(img.Stride) * img.Height];
System.Runtime.InteropServices.Marshal.Copy(img.Scan0, bmpBytes, 0, bmpBytes.Length);
for (int y = 0; y < img.Height; y++)
{
for (int x = 0; x < img.Width; x+=n*2)
{
cx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
if (x + n <= img.Width - 1) nx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
BitConverter.GetBytes(nx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + x * 4);
if (x + n <= img.Width - 1) BitConverter.GetBytes(cx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + (x + n) * 4);
}
}
System.Runtime.InteropServices.Marshal.Copy(bmpBytes, 0, img.Scan0, bmpBytes.Length);
bmp.UnlockBits(img);
}
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Bitmap bmp = new Bitmap(#"C:\Users\bluem\Downloads\Default.png");
for (int i = 0; i < bmp.Width; i++)
{
xPix(bmp, new Random().Next(20) + 1, System.Drawing.Color.White, System.Drawing.Color.Green);
}
Canvas.Image = bmp;
}
There's no such class as LockBitmap so I replaced it with the result of a call to Bitmap.LockBits directly.
The result of LockBits does not include functions for GetPixel and SetPixel, so I did what one normally does with the result of LockBits (see https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.7.2) and copied the data into a byte array instead.
When accessing the byte data directly, some math must be done to convert the x and y coordinates into a 1-dimensional coordinate within the array of bytes, which I did.
When accessing the byte data directly under the System.Drawing.Imaging.PixelFormat.Format32bppArgb pixel format, multiple bytes must be accessed to convert between byte data and a pixel color, which I did with BitConverter.GetBytes, BitConverter.ToInt32, Color.FromArgb and Color.ToArgb.
I don't think it's a good idea to be changing the Image in the middle of painting it. You should either be drawing the image directly during the Paint event, or changing the image outside the Paint event and allowing the system to draw it. So I used the OnClick of my form to trigger the function instead.
The first random number I got was 0, so I had to add 1 to avoid an endless loop.
The cx and nx parameters never seem to be used as inputs, so I put arbitrary color values in for them. Your x and y variables were not defined/declared anywhere.
If you want faster on-image-action, you can use Marshall.Copy method with Parallel.For
Why dont use GetPixel method? Because every time you call it, your ALL image is loaded to memory. GetPixel get one pixel, and UNLOAD all image. And in every iteration, ALL image is loaded to memory (for example, if u r working on 500x500 pix image, GetPixel will load 500x500 times whole pixels to memory). When you work on images with C# (CV stuff), work on raw bytes from memory.
I will show how to use with Lockbits in Binarization because its easy to explain.
int pixelBPP = Image.GetPixelFormatSize(resultBmp.PixelFormat) / 8;
unsafe
{
BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), ImageLockMode.ReadWrite, resultBmp.PixelFormat);
byte* ptr = (byte*)bmpData.Scan0; //addres of first line
int height = resultBmp.Height;
int width = resultBmp.Width * pixelBPP;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for(int x = 0; x < width; x = x + pixelBPP)
{
byte value = (offset[x] + offset[x + 1] + offset[x + 2]) / 3 > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x] = value;
offset[x + 1] = value;
offset[x + 2] = value;
if (pixelBPP == 4)
{
offset[x + 3] = 255;
}
}
});
resultBmp.UnlockBits(bmpData);
}
Now, example with Marshall.copy:
BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height),
ImageLockMode.ReadWrite,
resultBmp.PixelFormat
);
int bytes = bmpData.Stride * resultBmp.Height;
byte[] pixels = new byte[bytes];
Marshal.Copy(bmpData.Scan0, pixels, 0, bytes); //loading bytes to memory
int height = resultBmp.Height;
int width = resultBmp.Width;
Parallel.For(0, height - 1, y => //seting 2s and 3s
{
int offset = y * stride; //row
for (int x = 0; x < width - 1; x++)
{
int positionOfPixel = x + offset + pixelFormat; //remember about pixel format!
//do what you want with pixel
}
}
});
Marshal.Copy(pixels, 0, bmpData.Scan0, bytes); //copying bytes to bitmap
resultBmp.UnlockBits(bmpData);
Remember, when you warking with RAW bytes very important is to remember about PixelFormat. If you work on RGBA image, you need to set up every channel. (for example offset + x + pixelFormat). I showed it in Binarization example, how to deak with RGBA image with raw data. If lockbits are not fast enough, use Marshall.Copy

C# Bitmap GetPixel(), SetPixel() in GPU

I am using Cudafy as c# wrapper
I need to get colour info InputBitmap0.GetPixel(x, y) of a bitmap and make an new bitmap for output .
I need the following work to be done in GPU.
IN CPU
OutputBitmap.SetPixel(object_point_x, object_point_y, InputBitmap0.GetPixel(x, y));
In short:
How to GetPixel() for each pixel point of the input Bitmap, SetPixel() for each pixel point of the outputbitmap Bitmap in GPU.
OutputBitmap.SetPixel(object_point_x, object_point_y, InputBitmap0.GetPixel(x, y))
It took time but finally, I , cracked my case.
We have two Bitmap : one for output OutputBitmap and another for input InputBitmap0
Lets divide this task into parts:
do InputBitmap0.GetPixel() for x ,y coordinate
then , OutputBitmap.SetPixel() for a different coordinate object_point_x, object_point_y
Cudafy does not support Bitmap or Color type data. So I converted the Bitmaps to byte type.
BitmapData InputBitmapData0 = InputBitmap0.LockBits(new Rectangle(0, 0, InputBitmap0.Width, InputBitmap0.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
IntPtr ptr0 = InputBitmapData0.Scan0;//pointer for color
int stride0 = InputBitmapData0.Stride;
byte[] input_ragba_color = new byte[InputBitmapData0.Stride * InputBitmap0.Height];
Marshal.Copy(ptr0, input_ragba_color, 0, bytes0);// Copy the RGB values of color value into the array.
We have copied the content of the InputBitmap0 to the rgbValues array. Now we need to do the work of GetPixel() (get the values of R,G,B,A).
We need to do the above work ( make array) for OutputBitmap too because we will be doing SetPixel() in GPU but we will copy the array back to the bitmap later.
BitmapData OutputBitmapData = OutputBitmap.LockBits(new Rectangle(0, 0, OutputBitmap.Width, OutputBitmap.Height), ImageLockMode.WriteOnly, OutputBitmap.PixelFormat);
IntPtr ptr_output = OutputBitmapData.Scan0;
byte[] output_ragba = new byte[OutputBitmapData.Stride * OutputBitmap.Height];
Its GPU time for calculation. Lets initialize gpu.
CudafyModule km = new CudafyTranslator.Cudafy();
GPGPU gpu = new CudafyHost.getDevice(CudafyModes.Target, CudafyModes.DeviceId);
gpu.LoadModule(km);
Now send input_ragba_color and output_ragba to the gpu because we can iterate the array and do any calculation.
byte[] dev_output_rgba_color = gpu.Allocate<byte>(output_ragba.Length);
byte[] dev_input_ragba_color = gpu.CopyToDevice(input_ragba_color);
gpu.Launch(N, 1).update_bitmap(x, y, object_point_x, object_point_y,int stride0, int OutputBitmapData.Stride,dev_input_ragba_color,dev_output_rgba_color);
Now inside GPU(kernel)
[Cudafy]
public static void update_bitmap(GThread thread, int x,int y,int object_point_x,int object_point_y,int stride0, int OutputBitmapData_Stride,byte [] dev_input_ragba_color,byte [] dev_output_rgba_color)
{
dev_output_rgba_color[(object_point_y * OutputBitmapData_Stride) + (object_point_x * 4)] = input_ragba_color[(y * stride0) + (x * 4)];
dev_output_rgba_color[(object_point_y * OutputBitmapData_Stride) + (object_point_x * 4) + 1] = input_ragba_color[(y * stride0) + (x * 4) + 1];
dev_output_rgba_color[(object_point_y * OutputBitmapData_Stride) + (object_point_x * 4) + 2] = input_ragba_color[(y * stride0) + (x * 4) + 2];
dev_output_rgba_color[(object_point_y * OutputBitmapData_Stride) + (object_point_x * 4) + 3] = input_ragba_color[(y * stride0) + (x * 4) + 3];
}
I am taking values of each R,G,B,A ,ex: input_ragba_color[(y *
stride0) + (x * 4) + 1] which is solving 1st task
(InputBitmap0.GetPixel())
dev_output_rgba_color is taking the values of input_ragba_color
example:
dev_output_rgba_color[(object_point_y * OutputBitmapData_Stride) + (object_point_x * 4)] = input_ragba_color[(y * stride0) + (x * 4)];
which is solves our 2nd task (OutputBitmap.SetPixel())
We now know that gpu has populated an array(dev_output_rgba_color) for our OutputBitmap.
gpu.CopyFromDevice(dev_output_rgba_color, output_ragba); //dev_output_rgba_color values will be assigned to output_ragba
gpu.FreeAll();
Copy the result back to the OutputBitmap using the memory pointer and unlock it from the memory.
Marshal.Copy(output_ragba, 0, ptr_output, output_bytes);// Copy the RGB values of color value into the array.
OutputBitmap.UnlockBits(OutputBitmapData);
Now the OutputBitmap contains the updated values.
I think you will need to use a byte[] and allocate that on the GPU. I've seen you asking around and this answer is a work in progress, I'll keep updating it over the next few days as I get time.
CudafyModule km = new CudafyTranslator.Cudafy();
GPGPU gpu = new CudafyHost.getDevice(CudafyModes.Target, CudafyModes.DeviceId);
gpu.LoadModule(km);
var image = new Bitmap(width, height);
image = (Bitmap)Image.FromFile(#"C:\temp\a.bmp", true);
byte[] imageBytes = new byte[width * height * 4];
using(MemoryStream ms = new MemoryStream())
{
image.Save(ms, format);
imageBytes = ms.ToArray();
}
byte[] device_imageBytes = _gpu.CopyToDevice(imageBytes);
byte r = 0;
byte g = 0;
byte b = 0;
byte device_r = _gpu.Allocate<byte>(r);
byte device_g = _gpu.Allocate<byte>(g);
byte device_b = _gpu.Allocate<byte>(b);
//Call this in a loop
gpu.Launch(N, 1).GetPixel(x, y, device_imageBytes, device_r, device_g, device_b);
...
[Cudafy]
public static void GetPixel(GThread thread, int x, int y, byte[] imageBytes, byte blue, byte green, byte red)
{
int offset = x * BPP + y * stride;
blue = imageBytes[offset++];
green = imageBytes[offset++];
red = imageBytes[offset];
double R = red;
double G = green * 255;
double B = blue * 255 * 255;
}

RenderTargetBitmap with format rgba32

I am not very familiar with bitmaps and I need to save a FrameworkElement (specificaly Grid) as bitmap and copy it to buffer. The problem is I need to save it in Rgba format, not Pgrba, which isn't supported in RenderTargetBitmap. Relevant code is here:
_targetBitmap = new RenderTargetBitmap(xres, yres, 96, 96, PixelFormats.Pbgra32);
_targetBitmap.Clear();
// Child is grid
_targetBitmap.Render(Child);
// copy the pixels into the buffer
_targetBitmap.CopyPixels(new Int32Rect(0, 0, xres, yres), bufferPtr, _bufferSize, _stride);
I tried using WriteableBitmap, but I didn't how to render the Child. Any suggestions?
The CopyPixels function is already giving you direct access to the pixel data, so all you need to do is convert between formats. In this case, you need to swap the channel orders around and undo the premultiplication of alpha values.
NOTE: This code assumes your bufferPtr is a byte array or a byte pointer.
for (int y = 0; y < yres; y++)
{
for (int x = 0; x < xres; x++)
{
// Calculate array offset for this pixel
int offset = y * _stride + x * 4;
// Extract individual color channels from pixel value
int pb = bufferPtr[offset];
int pg = bufferPtr[offset + 1];
int pr = bufferPtr[offset + 2];
int alpha = bufferPtr[offset + 3];
// Remove premultiplication
int r = 0, g = 0, b = 0;
if (alpha > 0)
{
r = pr * 255 / alpha;
g = pg * 255 / alpha;
b = pb * 255 / alpha;
}
// Write color channels in desired order
bufferPtr[offset] = (byte)r;
bufferPtr[offset + 1] = (byte)g;
bufferPtr[offset + 2] = (byte)b;
bufferPtr[offset + 3] = (byte)alpha;
}
}

Using LockBits generates weird image

I'm trying to write out a grayscale image using Lockbits, my current code looks is
/// <summary>
/// Save the content of the FrameProc out to a bitmap
/// </summary>
public void Save(string path)
{
Bitmap bmp = new Bitmap(this.size.Width, this.size.Height
,PixelFormat.Format32bppRgb);
var data = bmp.LockBits(this.size, ImageLockMode.WriteOnly, bmp.PixelFormat);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
byte value = (byte)this.buffer[y, x];
row[x*Bits+r] = value;
row[x*Bits+g] = value;
row[x*Bits+b] = value;
}
}
}
bmp.UnlockBits(data);
bmp.Save(path, ImageFormat.Bmp);
}
where
/// <summary>
/// The amount of Bytes per pixel in the image
/// </summary>
private const int Bits = 4;
/// <summary>
/// Image components
/// </summary>
private const int a=3, r = 2, g = 1, b = 0;
However the image i receive is not correct:
Maybe this is related to how i'm reading them in? So here's that code
public FrameProc(Bitmap bmp)
{
this.size=new Rectangle(new Point(0,0), bmp.Size);
var data = bmp.LockBits(this.size
,ImageLockMode.ReadOnly
,bmp.PixelFormat);
this.buffer = new Matrix(this.size.Height, this.size.Width);
unsafe
{
for (int y = 0; y < this.size.Height; y++)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < this.size.Width; x++)
{
this.buffer[y,x] = 0.299*row[x*Bytes+r]
+ 0.587*row[x*Bytes+g]
+ 0.114*row[x*Bytes+b];
}
}
}
bmp.UnlockBits(data);
}
From the results you're getting - it looks exactly as if each pixel is three bytes big and not four as you have declared it - and as one would expect. (Note: you called it Bits - but that's wrong - it should be namned Bytes, not Bits).
I'd experiment with any one of this:
change from 4 to 3 bytes
change from Format32bppRgb to Format32bppArgb and fill out the alpha with 255
change from 4 to 3 bytes and from Format32bppRgb to from Format24bppRgb
I would also rewrite the loop slightly for performance (sorry, I can't help myself):
for (int x = 0; x < this.size.Width; x++, row += Bits)
{
byte value = (byte)this.buffer[y, x];
row[r] = value;
row[g] = value;
row[b] = value;
}
But were you really would get more speed if you get a pointer to this.buffer using the fixed keyword. Yes, you're not having any performance problems, but I couldn't help myself from mentioning it!
Use this function indeed:
public Bitmap MakeGrayscale(Bitmap original)
{
unsafe
{
//create an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(original.Width, original.Height);
//lock the original bitmap in memory
BitmapData originalData = original.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
//lock the new bitmap in memory
BitmapData newData = newBitmap.LockBits(
new Rectangle(0, 0, original.Width, original.Height),
ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
//set the number of bytes per pixel
// here is set to 3 because I use an Image with 24bpp
int pixelSize = 3;
for (int y = 0; y < original.Height; y++)
{
//get the data from the original image
byte* oRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* nRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < original.Width; x++)
{
//create the grayscale version
byte grayScale =
(byte)((oRow[x * pixelSize] * .11) + //B
(oRow[x * pixelSize + 1] * .59) + //G
(oRow[x * pixelSize + 2] * .3)); //R
//set the new image's pixel to the grayscale version
nRow[x * pixelSize] = grayScale; //B
nRow[x * pixelSize + 1] = grayScale; //G
nRow[x * pixelSize + 2] = grayScale; //R
}
}
//unlock the bitmaps
newBitmap.UnlockBits(newData);
original.UnlockBits(originalData);
return newBitmap;
}
}
Source and other interesting examples (with theory behind) could be taken from here

Categories

Resources