-1

Update: Clemens's solution is the fastest. I'll leave the alternative I found just in case:

While trying to create a minimal reproducible example as Peter Duniho suggested in the comment, I found that the wrong transparency values were coming from theBitmapImageToBitmapConverter() It was messing up the whole image. I now load the png straight to a bitmap and scan it and it gives accurate results:

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

Question was:

I have a few image controls in a list:

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

They load images from ".png" files like so:

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;
        }

I then convert those to bitmaps using this converter:

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);
    }
}

To later scan each pixel for transparency using this code:

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;
}

Basically, what should I do to get an accurate transparent pixels percentage/count from a bitmap?

I'm looking for the count of absolutely transparent pixels, not semi-transparent.

I'm not really looking for speed here so any solution goes. I'm already using unsafe code, so that's welcome too.

Tony Steel
  • 123
  • 1
  • 6
  • 1
    Why not just converting your image to a 32bpp bitmap and scanning the alpha channel? – dymanoid Sep 28 '19 at 23:14
  • If I understand correctly I would convert the image to 32bpp and then scan the same way as I have, using `image.GetPixel`? Should I still use `MakeTransparent()`? If possible also provide me with some references or code for the 32bpp conversion and your suggestion overall. I'll give it a try tomorrow. – Tony Steel Sep 28 '19 at 23:26
  • 1
    `GetPixel` is slow as hell. Better convert your image to a `WriteableBitmap` and access the image data directly. See e.g. [this answer](https://stackoverflow.com/a/20182830/2846483) for an example. – dymanoid Sep 29 '19 at 00:01
  • If you want help from Stack Overflow, you'll need to improve your question. Provide a good [mcve], for one. For another, you'll need to explain your goal better. "Transparency" in the context of a PNG file is not a binary thing. Each pixel can have an alpha ranging from 0% transparent to 100% transparent. You seem to want an "average transparency" result, but that would involve averaging all of the alpha components from each pixel, not looking just for 0 alpha values. It's also not clear why you both creating `transpColorValue`; you only ever add values to it, you don't ever _use_ those values – Peter Duniho Sep 29 '19 at 02:39

1 Answers1

0

You neither need a System.Drawing.Bitmap nor a WriteableBitmap to access the pixel values in a BitmapSource.

Just call CopyPixels to get the pixel buffer and count the pixels with an alpha value of 0. First make sure you access the buffer in the desired format:

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;
}
Clemens
  • 123,504
  • 12
  • 155
  • 268