Получить процент прозрачности растрового изображения

Обновление: решение Клеменса является самым быстрым. На всякий случай оставлю вариант, который нашел:

Пытаясь создать минимальный воспроизводимый пример, как предложил Питер Дунихо в комментарии, я обнаружил, что неправильные значения прозрачности исходили от BitmapImageToBitmapConverter() Это портило все изображение. Теперь я загружаю png прямо в растровое изображение и сканирую его, и это дает точные результаты:

Bitmap bmp = new Bitmap("icon.png");
Console.WriteLine(TransparencyPercentageInImage(bmp));

Вопрос был:

У меня есть несколько элементов управления изображениями в списке:

imagesList[index].Source = ReloadIcon(index);

Они загружают изображения из файлов «.png» следующим образом:

public BitmapImage ReloadIcon(int index)
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
            image.UriSource = new Uri(iconPaths[index], UriKind.Absolute);
            image.EndInit();
            return image;
        }

Затем я конвертирую их в растровые изображения с помощью этого конвертера:

private Bitmap BitmapImageToBitmapConverter(BitmapImage bitmapImage)      
{
    using (MemoryStream outStream = new MemoryStream())
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapImage));
        enc.Save(outStream);
        System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);

        return new Bitmap(bitmap);
    }
}

Чтобы позже сканировать каждый пиксель на прозрачность, используя этот код:

private double TransparencyPercentageInImage(Bitmap image)
{
    double transpPercentage;
    double transpPixelCount = 0;
    double totalPixelCount = image.Height * image.Width;
    Console.WriteLine("Total pixel count: " + totalPixelCount);

    for (int y = 0; y < image.Height; ++y)
    {
        for (int x = 0; x < image.Width; ++x)
        {
            if (image.GetPixel(x, y).A == 0) //or !=255
            {
                transpPixelCount++;
            }
        }
    }

    transpPercentage = transpPixelCount / totalPixelCount * 100;
    return transpPercentage;
}

В принципе, что мне делать, чтобы получить точный процент / количество прозрачных пикселей из растрового изображения?

Я ищу количество абсолютно прозрачных пикселей, а не полупрозрачных.

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


person Tony Steel    schedule 28.09.2019    source источник
comment
Почему бы просто не преобразовать ваше изображение в 32-битное растровое изображение и не просканировать альфа-канал?   -  person dymanoid    schedule 29.09.2019
comment
Если я правильно понимаю, я бы преобразовал изображение в 32bpp, а затем сканировал бы так же, как у меня, используя image.GetPixel? Должен ли я по-прежнему использовать MakeTransparent()? Если возможно, также предоставьте мне некоторые ссылки или код для преобразования 32bpp и ваше предложение в целом. Я попробую завтра.   -  person Tony Steel    schedule 29.09.2019
comment
GetPixel чертовски медленный. Лучше преобразовать изображение в WriteableBitmap и получить прямой доступ к данным изображения. См., например. этот ответ для примера.   -  person dymanoid    schedule 29.09.2019
comment
Если вам нужна помощь от Stack Overflow, вам нужно улучшить свой вопрос. Например, приведите хороший минимально воспроизводимый пример. Во-вторых, вам нужно будет лучше объяснить свою цель. Прозрачность в контексте файла PNG не является бинарной вещью. Каждый пиксель может иметь альфа-канал в диапазоне от 0% прозрачности до 100% прозрачности. Кажется, вам нужен средний результат прозрачности, но это потребует усреднения всех альфа-компонентов каждого пикселя, а не поиска только нулевых альфа-значений. Также непонятно, почему вы оба создаете transpColorValue; вы только когда-либо добавляете к нему значения, вы никогда не используете эти значения   -  person Peter Duniho    schedule 29.09.2019


Ответы (1)


Вам не нужны ни System.Drawing.Bitmap, ни WriteableBitmap для доступа к значениям пикселей в BitmapSource.

Просто вызовите CopyPixels, чтобы получить буфер пикселей и подсчитать пиксели с альфа-значением, равным 0. Сначала убедитесь, что вы обращаетесь к буферу в нужном формате:

private double GetTransparentPixelsPercentage(BitmapSource bitmap)
{
    if (bitmap.Format != PixelFormats.Bgra32)
    {
        bitmap = new FormatConvertedBitmap(bitmap, PixelFormats.Bgra32, null, 0);
    }

    var pixelCount = bitmap.PixelWidth * bitmap.PixelHeight;
    var pixels = new byte[4 * pixelCount];

    bitmap.CopyPixels(pixels, 4 * bitmap.PixelWidth, 0);

    var transparentPixelCount = 0;

    for (var i = 3; i < 4 * pixelCount; i += 4) // start at first alpha value
    {
        if (pixels[i] == 0)
        {
            transparentPixelCount++;
        }
    }

    return (double)transparentPixelCount / pixelCount;
}
person Clemens    schedule 29.09.2019
comment
Работает как шарм прямо из BitmapImage. Спасибо. - person Tony Steel; 29.09.2019