How to Manipulate Images at the Pixel Level in C#

How to manipulate images at the pixel level in C#

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.

black and white Image processing in C#

Morphological filtering would work great as long as you are working with binary images like the one you provided. Dilate the image and then subtract the original.

alt text http://img29.imageshack.us/img29/1420/morphf.png

Here's a MATLAB example:

lImage = zeros(19, 19, 3);
lImage(7:13, 7:13, :) = repmat( ...
[0 0 1 1 1 0 0; ...
0 1 1 1 1 1 0; ...
1 1 1 1 1 1 1; ...
1 1 1 1 1 1 1; ...
1 1 1 1 1 1 1; ...
0 1 1 1 1 1 0; ...
0 0 1 1 1 0 0;], [1 1 3]);
figure; imshow(lImage);
lOutline = imdilate(lImage, strel('disk', 1)) - lImage;
lOutline(:, :, 2:3) = 0;
figure; imshow(lImage + lOutline);

How can I read image pixels' values as RGB into 2d array?

Well, if I understood correctly, you want to iterate through the pixels in the image, perform some kind of test, and if it passes you want to store that pixel in an array. Here´s how you could do that:

using System.Drawing;

Bitmap img = new Bitmap("*imagePath*");
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color pixel = img.GetPixel(i,j);

if (pixel == *somecondition*)
{
**Store pixel here in a array or list or whatever**
}
}
}

Don´t think you need anything else. If you need the specific RGB values you can get them from the corresponding methods in the pixel object.

library for editing photos

Try this tutorial. this will help you to do normal jobs with images in a tutorial format using C#:

http://www.switchonthecode.com/tutorials/csharp-tutorial-image-editing-rotate

for working with images in pixel level see this thread :

How to manipulate images at the pixel level in C#

Right way to parallelize pixel access across multiple images using ImageSharp

Following up on my own question, I had a chance to work through @James's answer and made some discoveries that I thought might be useful to share. If you don't give a shit about what is happening and just want code, skip to the end.

First, I looked into his suggestion to avoid using the Advanced namespace, and instead consider using the ProcessPixelRowsAsVector4 variant of the higher level pixel buffer manipulation API. I discovered that I couldn't use that because in my case I need the row index (ie. int y) to do my calculations, and ProcessPixelRowsAsVector4 doesn't provide it.

I opened a discussion here about providing an overload of ProcessPixelRowsAsVector4 that actually does have the row index, so it's possible by the time you're reading this that the library actually has a signature like this that you should try using.


Meanwhile, in the present, I went about implementing James's other suggestion, the ParallelRowIterator.IterateRows solution.

I did get it working, but as I was putting final touches, I realized that Invoke(int y, Span<Rgba32> span) was giving me a span that I wasn't using. What is that span? Why was I not using it? Could I use it? Should I discard it?

This mattered to me because in my case these spans could be between 25,000 and 50,000 pixels long, and about that same number of spans could be allocated, so not doing that if it wasn't necessary seemed like a good idea. (It was possible that it's just returning a pointer to a particular place in already-allocated memory, which makes this less of an issue, but I wanted to know.)

My first guess was that the normal usecase for IterateRows involved looping over a source image, so maybe that span was like sourceRowSpan, and if that was true maybe I could somehow get the iterator to loop over the destination image and just return destinationRowSpan without me having to additionally get it inside the loop using DangerousGetRowSpan which sounds like the kind of method call that wears a leather jacket and disrespects your mother.

But I couldn't see how the iterator was even choosing where to get the span from at all, like my source and destination buffers were just members on my custom RowOperation class with no special relationship to the IRowOperation interface.

So I looked inside the ParallelRowIterator class and followed the rabbit hole into RowOperationWrapper which seemed to be what was actually calling the Invoke, and I discovered 3 things:

  1. The span being passed in was just a newly allocated memory span of whatever pixel type. So it WAS, in fact, allocating a bunch of memory for this and not just returning a pointer to the memory.
  2. The span wasn't referencing either the source or destination image really, so I could safely ignore it if I wanted, which the originally suggested code de facto did, but what I mean is that I could explicitly discard it with _ if I wanted.
  3. There is a whole other signature that doesn't allocate or pass a span at all! It just passes the row index, which is all I actually need!

So having discovered that, I modified the originally suggested code as follows:

using Image<Rgba32> source = new(100, 100);
using Image<Rgba32> destination = new(100, 100);

Configuration configuration = Configuration.Default;

// You need access to individual frame pixel buffers in order
// to access some of the advanced APIs
RowOperation operation = new RowOperation(
configuration,
source.Frames[0].PixelBuffer,
destination.Frames[0].PixelBuffer);

// Ensure we don't go out of bounds
var interest = Rectangle.Intersect(source.Bounds(), destination.Bounds());
ParallelRowIterator.IterateRows<RowOperation>(
configuration,
interest,
in operation);

// Save the output.
private readonly struct RowOperation : IRowOperation
{
private readonly Random random;
private readonly Buffer2D<Rgba32> source;
private readonly Buffer2D<Rgba32> destination;
private readonly Configuration configuration;

public RowOperation(
Configuration configuration,
Buffer2D<Rgba32> source,
Buffer2D<Rgba32> destination)
{

this.source = source;
this.destination = destination;
this.random = new();
this.configuration = configuration;
}

public void Invoke(int y)
{
Span<Rgba32> destinationRowSpan = this.destination.DangerousGetRowSpan(y);
for (int x = 0; x < destinationRowSpan.Length; x++)
{
destinationRowSpan[x] = this.GetRandomPixel();
}
}

private Rgba32 GetRandomPixel()
{
int y = this.random.Next(this.source.Height);
int x = this.random.Next(this.source.Width);
return this.source[x, y];
}
}

Basically I made RowOperation implement IRowOperation instead of IRowOperation<Rgba32>, and invoked it with .IterateRows<RowOperation>() instead of .IterateRows<RowOperation, Rgba32>(), which invokes with just the row index instead of the row index plus Span object.



Related Topics



Leave a reply



Submit