Remove transparency in images with C#
Asked Answered
M

7

13

does anyone know a smooth / fast way of removing transparency from e.g. pngs/tiffs etc and replacing it with a white background?

Basically what I need this for is I need to create PDF/A compatible images, which may, according to the spec, have -no- transparency (and therefore a fixed white background is fine).

Any ideas / suggestions?

Cheers & thanks, -Jörg

Metallophone answered 6/3, 2009 at 9:51 Comment(0)
M
23

You could create a bitmap the same size as the png, draw a white rectangle and then draw the image on top of it.

void RemoveImageTransparancy(string file) {
    Bitmap src = new Bitmap(file);
    Bitmap target = new Bitmap(src.Size.Width,src.Size.Height);
    Graphics g = Graphics.FromImage(target);
    g.DrawRectangle(new Pen(new SolidBrush(Color.White)), 0, 0, target.Width, target.Height);
    g.DrawImage(src, 0, 0);
    target.Save("Your target path");
}
Mutiny answered 6/3, 2009 at 10:4 Comment(4)
Insterad of drawing a rectangle, you could just call g.Clear(Color.White);Kellyekellyn
Awesome - thanks for the quick reply! For some reason I had to specify the width and height for the g.DrawImage, too... otherwise the placed pictures where for some reason smaller than their original .width/.height)Metallophone
Comment by Guffa is the way to go.Featherbrain
Transparency in an image is the alpha channel. This solution does not remove this channel, so it remains a transparent image without transparent areas. To remove transparency you have to remove the alpha channel.Korten
K
12

You have to remove the alpha channel. Otherwise you'll still have a transparent image - just without transparent areas.

class Program
{
    static void Main(string[] args)
    {
        //this also works for different file formats
        ReplaceTransparency(@"C:\Y\transparent.png", System.Drawing.Color.White).Save(@"C:\Y\no_transparency.png");
        ReplaceTransparency(@"C:\Y\transparent.gif", System.Drawing.Color.White).Save(@"C:\Y\no_transparency.gif");
    }

    public static System.Drawing.Bitmap ReplaceTransparency(string file, System.Drawing.Color background)
    {
        return ReplaceTransparency(System.Drawing.Image.FromFile(file), background);
    }

    public static System.Drawing.Bitmap ReplaceTransparency(System.Drawing.Image image, System.Drawing.Color background)
    {
        return ReplaceTransparency((System.Drawing.Bitmap)image, background);
    }

    public static System.Drawing.Bitmap ReplaceTransparency(System.Drawing.Bitmap bitmap, System.Drawing.Color background)
    {
        /* Important: you have to set the PixelFormat to remove the alpha channel.
         * Otherwise you'll still have a transparent image - just without transparent areas */
        var result = new System.Drawing.Bitmap(bitmap.Size.Width, bitmap.Size.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        var g = System.Drawing.Graphics.FromImage(result);

        g.Clear(background);
        g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
        g.DrawImage(bitmap, 0, 0);

        return result;
    }
}
Korten answered 3/2, 2016 at 13:54 Comment(0)
P
1

Adding to Stormenet's answer; remember to wrap all Bitmap and Graphics objects with "using" statement to dispose unmanaged resources.

Positron answered 23/5, 2012 at 23:13 Comment(0)
F
0

1) Create a bitmap with a white background and with the same size as your image
2) Load you image and paint it on top of your "white" bitmap
3) Save the newly created image

Finally answered 6/3, 2009 at 10:2 Comment(0)
B
0

PNGs have alpha channel, so simple recoloring won't do. Create white image of same size, and create composite image overlay your image over it.

Berar answered 6/3, 2009 at 10:4 Comment(0)
W
0

namespaces:

using Microsoft.Win32;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using PdfSharp;
using PdfSharp.Pdf;
using PdfSharp.Drawing;

Creating PNG or TIFF BitmapSource from File:

BitmapSource BitmapSource;
private void OpenFile(Object sender, RoutedEventArgs e)
    {
        OpenFileDialog OpenFileDialog = new OpenFileDialog();
        OpenFileDialog.Filter = "PNG files (*.png)|*.png|TIFF files (*.tif)|*.tif";            

        if (OpenFileDialog.ShowDialog() == true)
        {
            try
            {
                if (OpenFileDialog.OpenFile() != null)
                {
                    String InitialPath = OpenFileDialog.FileName;                       
                    FileStream InitialFileStream = new FileStream(InitialPath, FileMode.Open, FileAccess.Read, FileShare.Read);

                    switch (OpenFileDialog.FilterIndex)
                    {
                        case 1:
                            PngBitmapDecoder PngBitmapDecoder = new PngBitmapDecoder(InitialFileStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                            BitmapSource = PngBitmapDecoder.Frames[0];
                            InitialFileStream.Close();
                            break;
                        case 2:
                            TiffBitmapDecoder TiffBitmapDecoder = new TiffBitmapDecoder(InitialFileStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                            BitmapSource = TiffBitmapDecoder.Frames[0];
                            InitialFileStream.Close();
                            break;
                    }  
                }
            }
            catch (Exception Exception)
            {
                MessageBox.Show("Error: Could not read file from disk. Original error: ", Exception.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }

Functions on Button Click:

private void ButtonClick(Object sender, RoutedEventArgs e)
    {
        PixelFormat PixelFormat = BitmapSource.Format;
        if (PixelFormat == PixelFormats.Bgra32)
        {
            try
            {
                BitmapSource = Bgra32ToBgra24(BitmapSource);
                //BitmapSource = Bgra32ToGray8(BitmapSource);
            }

            catch (Exception Exception)
            {
                MessageBox.Show("Error: Could not convert BitmapSource. Original error: ", Exception.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }

Function:

public static BitmapSource Bgra32ToBgr24(BitmapSource BitmapSource)
    {
        Int32 PixelWidth = BitmapSource.PixelWidth;
        Int32 PixelHeight = BitmapSource.PixelHeight;
        Double DpiX = BitmapSource.DpiX;
        Double DpiY = BitmapSource.DpiY;

        PixelFormat InputPixelFormat = BitmapSource.Format;
        BitmapPalette InputPalette = BitmapSource.Palette;
        Int32 InputBitsPerPixel = BitmapSource.Format.BitsPerPixel;
        Int32 InputStride = PixelWidth * InputBitsPerPixel / 8;
        Byte[] InputPixelsArray = new Byte[InputStride * PixelHeight];
        BitmapSource.CopyPixels(InputPixelsArray, InputStride, 0);

        PixelFormat PixelFormat = PixelFormats.Bgr24;
        BitmapPalette Palette = null;
        Int32 BitsPerPixel = 24;
        Int32 Stride = PixelWidth * BitsPerPixel / 8;
        Byte[] PixelsArray = new Byte[InputStride * PixelHeight / 4 * 3]; 

        Int32 i = 0; Int32 j = 0; Int32 k = 0;
        while (i < InputPixelsArray.Length / 4)
        {
            PixelsArray[k] = InputPixelsArray[j];
            PixelsArray[k + 1] = InputPixelsArray[j + 1];
            PixelsArray[k + 2] = InputPixelsArray[j + 2];

            i = i + 1;
            j = j + 4;
            k = k + 3;
        }

        BitmapSource = BitmapSource.Create(PixelWidth, PixelHeight, DpiX, DpiY, PixelFormat, Palette, PixelsArray, Stride);
        return BitmapSource;
    }

Converting A channel to Gray8 BitmapSource

public static BitmapSource Bgra32ToGray8(BitmapSource BitmapSource)
    {
        Int32 PixelWidth = BitmapSource.PixelWidth;
        Int32 PixelHeight = BitmapSource.PixelHeight;
        Double DpiX = BitmapSource.DpiX;
        Double DpiY = BitmapSource.DpiY;

        PixelFormat InputPixelFormat = BitmapSource.Format;
        BitmapPalette InputPalette = BitmapSource.Palette;
        Int32 InputBitsPerPixel = BitmapSource.Format.BitsPerPixel;
        Int32 InputStride = PixelWidth * InputBitsPerPixel / 8;
        Byte[] InputPixelsArray = new Byte[InputStride * PixelHeight];
        BitmapSource.CopyPixels(InputPixelsArray, InputStride, 0);

        PixelFormat PixelFormat = PixelFormats.Gray8;
        BitmapPalette Palette = null;
        Int32 BitsPerPixel = 8;
        Int32 Stride = PixelWidth * BitsPerPixel / 8;
        Byte[] A_PixelsArray = new Byte[InputStride * PixelHeight / 4];

        Int32 i = 0; Int32 j = 3;
        while (i < InputPixelsArray.Length / 4)
        {
            A_PixelsArray[i] = InputPixelsArray[j];

            i = i + 1;
            j = j + 4;
        }

        BitmapSource = BitmapSource.Create(PixelWidth, PixelHeight, DpiX, DpiY, PixelFormat, Palette, A_PixelsArray, Stride);
        return BitmapSource;
    }

Saving BitmapSource to PDF:

private void SaveFileAs(Object sender, RoutedEventArgs e)
    {
        SaveFileDialog SaveFileDialog = new SaveFileDialog();
        SaveFileDialog.Filter = "PDF files (*.pdf)|*.pdf";
        if (SaveFileDialog.ShowDialog() == true)
        {
            try
            {
                if (SaveFileDialog.FileName != null)
                {
                    String DestinationPath = SaveFileDialog.FileName;
                    FileStream DestinationFileStream = new FileStream(DestinationPath, FileMode.Create, FileAccess.Write, FileShare.Write);

                    switch (SaveFileDialog.FilterIndex)
                    {   
                        case 1:
                            PdfDocument PdfDocument = new PdfDocument();
                            PdfPage PdfPage = new PdfPage();
                            PdfDocument.Pages.Add(PdfPage);
                            XImage Image = XImage.FromBitmapSource(BitmapSource);
                            XGraphics XGraphic = XGraphics.FromPdfPage(PdfDocument.Pages[0]);

                            Double VerticalMargin = 20;
                            Double HorizontalMargin = 20;
                            Double Ratio = BitmapSource.Height / BitmapSource.Width;
                            Double ImageWidth = PdfPage.Width - 2 * VerticalMargin;
                            Double ImageHeight = Ratio * (PdfPage.Width - 2 * HorizontalMargin);

                            XGraphic.DrawImage(Image, VerticalMargin, HorizontalMargin, ImageWidth, ImageHeight);
                            PdfDocument.Save(DestinationFileStream);
                            PdfDocument.Close();
                            DestinationFileStream.Close();
                            break;
                    }
                }
            }
            catch (Exception Exception)
            {
                MessageBox.Show("Error: Could not write file to disk. Original error: ", Exception.Message, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
Woodbridge answered 22/7, 2014 at 22:18 Comment(1)
Your code is inspiring. Since I'm dealing with a similar problem, just a different in the context because my input is TIFF in CMYK + A, I would still give it a try with copypixels and modify bytes buffer array afterward. If you have time, please take a look at my question #65266682Meris
G
0

PDF in general uses 2 storage types for 2 basic older compressions. One is /Flate (a gzip variant) for bitmaps of any number of colour layers. The other is /DCT (older baseline JPEG).

The better of the two for storage is /DCT as it is one reversible (extractable) compressed insertion. (Whereas transparent bitmaps may be divided into 2 overlays). Thus if you convert, all images to 100% quality.JPEG with "NO subsampling", it will be as crisp as a PNG or TIFF OR GIF, but significantly less bytes and stripped of its transparency.

Here is a very small JPEG test piece to show there are no noticeable aberrations as single pixels. The main remaining issue then to watch out for is colour shift, which is why we aim for no subsampling of colours.

JPEG, quality: 100, subsampling OFF: enter image description here

You will need to zoom in to see the single pixels are not fuzzy or "haloed" by artefacts, simply should look like 6 colors. However due to JPEG recoding even at 100% quality it is seen as a 33 colour count.

Injected into a PDF at any scale it will be as good as any lossless image, but much smaller so this PDF is at any scale only 3,788 bytes and no transparency.

enter image description here

Gratulate answered 15/3, 2024 at 23:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.