[.net image processing] ImageSharp high-speed pixel-by-pixel processing

ImageSharp is a relatively complete image processing library. But there are still some requirements that cannot be directly realized through its API, and we need to do a little development by ourselves. Since no relevant articles were found on the Internet, this article will use "replacing one color in an image with another color" as an example.

The function of loading pictures provided by the ImageSharp library, pay attention to use the generic method to specify the type of color to be loaded. The options are Rgb24, Rgb64, Rgba32, Rgba64, etc., which can be selected according to needs.

Note that the Image here and below refers to SixLabors.ImageSharp.Image

var img = Image.Load("文件路径");
//Load()方法载入的Image对象,无法访问像素

var img = Image.Load<Rgba32>("文件路径");
//Load<TPixel>()方法载入的Image对象,可以访问像素

This object can use the indexer to read and write the color of each pixel position.

However, it is relatively inefficient to directly write a double-layer loop to traverse each pixel. Let's try to write a function like this:

public void Replace1(Rgba32 from, Rgba32 to)
{
    for (int row = 0; row < img.Height; row++)
    {
         for (int col = 0; col < img.Width; col++)
         {
              if (img[col,row] == from)
                   img[col,row] = to;
         }
    }
}

Call (loading and saving are omitted):

Rgba32 white = new(255,255, 255, 255);
Rgba32 black = new(0, 0, 0, 255);
p.Replace2(white,black);

Replace all white parts of the image with black. The test image is about 4000x4000 pixels.

It takes about 2.5 seconds, which is ten times or even nine times faster than System.Drawing. But we have a way to make it a bit faster.

The Image object provides a method called ProcessPixelRows with the comment: "Execute processPixels to safely and efficiently process image pixels."

The processPixel here is the parameter of this method, the type is PixelAccessorAction<Rgba32>, (the color type is determined by the color type of Image)

Let's try to construct one, and found that its constructor needs to fill in a delegate, which inputs a PixelAccessor object and has no return value. 

Write a lambda expression, x represents the PixelAccessor object. There are only three members in x, you can get the width and height of the image, and there is a GetRowSpan() method. We write the following code to accomplish what we want:

public void Replace(Rgba32 from,Rgba32 to)
{
    PixelAccessorAction<Rgba32> ac = new(x =>
    {
         for(int row = 0; row < x.Height; row++)
         { 
               var s = x.GetRowSpan(row);
               for(int col = 0; col < x.Width; col++)
               {
                   if (s[col]==from)
                       s[col] = to;
               }
         }
    });
}
img.ProcessPixelRows(ac);

It only took about 0.3 seconds to achieve exactly the same function.

Guess you like

Origin blog.csdn.net/qq_33794853/article/details/128372427