Замена цвета текстурой в ядре asp.net C#

Я экспериментировал с графическим процессором ImageSharp, поскольку System.Drawing недоступен в ядре asp.net, и System.Drawing может иметь свои проблемы.

Я хочу заполнить пробел определенной текстурой изображения. Код ниже работает, но мучительно медленно.

Поскольку я впервые обрабатываю изображения, я не знаю, какой самый эффективный способ сделать это.

Какой самый лучший и эффективный способ заполнить пустое пространство текстурой.

В результате этого: пончик

К этому: Sparkly Donut

    public void CreateImage()
    {
        var webRoot = _env.WebRootPath;
        var ImgSrc = "\\Images\\SampleImage.png";
        var TextureURL = "\\Images\\Starsinthesky.jpg";
        var file = webRoot + ImgSrc;
        var texture = webRoot + TextureURL;
        var myPath = Path.Combine(webRoot, ImgSrc);

        byte[] img;

        using (Image<Rgba32> image = Image.Load(file))
        {
            HashSet<Texture> textureArr = getRGBaBytes(texture, image);
            for (int h = 0; h <= image.Height; h++)
            {
                for(int w = 0; w <= image.Width; w++)
                {
                    if(image[w,h] == Rgba32.FromHex("#ffffff"))
                    {
                        image[w, h] = textureArr.Where(t => t.x == w && t.y == h).First().color;
                    }
                }
            }
            image.Save("NewImage.png");
        }
    }

    public HashSet<Texture> getRGBaBytes(string textureURL, Image<Rgba32> sample)
    {

        using (Image<Rgba32> tex = Image.Load(textureURL))
        {
            int bitsizelimit = int.MaxValue;    
            if (sample.Width > tex.Width || sample.Height > tex.Height)
            {
                throw new Exception("Texture image dimensions must be greater or equal to sample image");
            }

            HashSet<Texture> myTexture = new HashSet<Texture>();
            for (int h = 0; h <= sample.Height; h++)
            {
                for (int w = 0; w <= sample.Width; w++)
                {
                    System.Diagnostics.Debug.WriteLine($"{tex[w,h].ToHex()} at x:{w} y:{h}");
                    myTexture.Add(new Texture { color = tex[w, h], x = w, y = h });
                }
            }
            return myTexture;
        }
    }

    public class Texture
    {
        public Rgba32 color { get; set; }
        public int x { get; set; }
        public int y { get; set; }
    }

1 ответ

Решение

Я запустил небольшое консольное приложение, чтобы продемонстрировать, как просто добиться желаемого, но сначала я объясню, почему ваш подход медленный.

  1. getRGBaBytes не требуется. По сути, вы просматриваете оба изображения и создаете класс для каждого пикселя вашего текстурированного изображения. Это много распределения памяти!
  2. У вас есть запрос Linq в каждой пиксельной операции. Where а также First, Опять же, массивное выделение памяти для каждого пикселя в изображении. Там нет необходимости в этом.
  3. Вы делаете сравнение с новым Rgba32 struct каждый раз анализирует шестнадцатеричное значение, которое будет медленным. Это можно сделать с помощью статического Rgba32.White вместо структуры
static void Main(string[] args)
{
    System.IO.Directory.CreateDirectory("output");
    using (var img = Image.Load("LPUVf.png"))
    using (var texture = Image.Load("stars.jpg"))
    {
        if (img.Width >  texture.Width || img.Height > texture.Height)
        {
            throw new InvalidOperationException("Image dimensions must be less than or equal to texture dimensions!");
        }

        for (int y = 0; y < img.Height; y++)
        {
            for (int x = 0; x < img.Width; x++)
            {
                var pixel = img[x, y];
                if (pixel == Rgba32.White)
                {
                    img[x, y] = texture[x, y];
                }
            }
        }

        img.Save("output/LBUVf.png");
    }
}

Вот вывод из моего образца. (Я думаю, что на самом деле я использовал одно и то же изображение звездного пейзажа:)) Вы можете улучшить и уменьшить оставшиеся белые области, проверяя, Rgba32 компонент находится в пределах диапазона 255 но я оставлю это тебе.

Образец вывода

PS Пакет ImageSharp.Drawing содержит методы, которые позволяют рисовать текстуры на полигонах. Теоретически вы могли бы создать новое изображение с нуля, если бы знали размеры каждой составной детали.

Обновить:

Я не мог с собой поделать, поэтому я написал код, чтобы уменьшить оставшиеся пиксели.

static void Main(string[] args)
{
    System.IO.Directory.CreateDirectory("output");
    const int min = 128; // Grey midpoint
    using (var img = Image.Load("LPUVf.png"))
    using (var texture = Image.Load("stars.jpg"))
    {
        if (img.Width >  texture.Width || img.Height > texture.Height)
        {
            throw new InvalidOperationException("Image dimensions must be less than or equal to texture dimensions!");
        }

        for (int y = 0; y < img.Height; y++)
        {
            for (int x = 0; x < img.Width; x++)
            {
                var pixel = img[x, y];
                if (pixel.R >= min && pixel.G >= min && pixel.B >= min && pixel.A >= min)
                {
                    img[x, y] = texture[x, y];
                }
            }
        }

        img.Save("output/LBUVf.png");
    }
}

Как видите, это намного приятнее.

Обновленный образец с лучшим результатом

Другие вопросы по тегам