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:
- 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. - 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. - 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
How to Find a Java to C# Converter
How to Use Java-Style Throws Keyword in C#
Extending an Enum via Inheritance
C# Equivalent to Java's Charat()
Is There an Equivalent to the Scanner Class in C# for Strings
Posting JSON to Url via Webclient in C#
ASP.NET Core 6 How to Access Configuration During Startup
Disable JavaScript Error in Webbrowser Control
How to Request Administrator Permissions When the Program Starts
How to Work with Time Zones in ASP.NET
Where Does Error Cs0433 "Type 'X' Already Exists in Both A.Dll and B.Dll " Come From
How to Get Timestamp of Tick Precision in .Net/C#
Send Email Using System.Net.Mail Through Gmail
How to Filter Directory.Enumeratefiles with Multiple Criteria