首页 > 解决方案 > 为什么保存位图后颜色数量会发生变化?

问题描述

我正在创建一个单一用途的应用程序,它将拍摄一张图像并通过一些 Alpha 通道调整来保存它。调整是使用颜色矩阵进行的,旨在仅更改 Alpha 通道。我希望 otput 在 RGB 值方面与原始值相同。
我正在处理使用 Photoshop 或 After Effects 创建的 32 位 PNG 图像。我在没有任何压缩的情况下保存/渲染文件(至少这是我被告知的)。

这是两个颜色矩阵,可以帮助我说明问题。

            ColorMatrix harmlessCm = new ColorMatrix(new float[][]{
                new float[]{1, 0, 0, 0, 0},
                new float[]{0, 1, 0, 0, 0},
                new float[]{0, 0, 1, 0, 0},
                new float[]{0, 0, 0, 1, 0},
                new float[]{0, 0, 0, 0, 0}});

            ColorMatrix luminanceToAlphaCm = new ColorMatrix(new float[][]{
                new float[]{1, 0, 0, .2125f, 0},
                new float[]{0, 1, 0, .7154f, 0},
                new float[]{0, 0, 1, .0721f, 0},
                new float[]{0, 0, 0, 0, 0},
                new float[]{0, 0, 0, 0, 0}});

情景#1。文件:没有透明区域的 32 位 PNG,颜色数5707
应用harmlessCm对颜色数量没有任何影响。
应用 LEDluminanceToAlphaCm减少颜色数量:1052。

情景#2。文件:具有多个半透明区域(相同图像但没有背景)的 32 位 PNG,颜色数6244
应用harmlessCmled减少颜色数量:5990。
应用luminanceToAlphaCmled减少颜色数量:1470。

全班:

using Microsoft.WindowsAPICodePack.Dialogs;
using Prism.Commands;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace RGBA_Playground.ViewModels
{
    class MainViewModel : BaseViewModel
    {
        public string Before { get; private set; }
        public Image After { get; private set; } 

        public DelegateCommand UploadCommand { get; }
        public DelegateCommand SaveCommand { get; }

        public MainViewModel()
        {
            UploadCommand = new DelegateCommand(() =>
            {
                using (var dialog = new CommonOpenFileDialog())
                {
                    dialog.Multiselect = false;

                    if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
                    {
                        var image = dialog.FileName;
                        Before = image;
                        After = GetChangedImage(image);
                        RaisePropertyChanged("Before");
                        RaisePropertyChanged("After");
                    }
                }
            });

            SaveCommand = new DelegateCommand(() =>
            {
                var changedImage = GetChangedImage(Before);

                using (Bitmap bm = new Bitmap(changedImage))
                {
                    bm.Save(Path.GetDirectoryName(Before) + "\\" + Path.GetRandomFileName() + ".png", ImageFormat.Png);
                }
            });

        private static Bitmap GetChangedImage(string path)
        {
            Bitmap original = new Bitmap(path, false);
            Bitmap result = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
            result.SetResolution(original.HorizontalResolution, original.VerticalResolution);

            ColorMatrix cm = new ColorMatrix(new float[][]{
                new float[]{1, 0, 0, .2125f, 0},
                new float[]{0, 1, 0, .7154f, 0},
                new float[]{0, 0, 1, .0721f, 0},
                new float[]{0, 0, 0, 0, 0},
                new float[]{0, 0, 0, 0, 0}});

            using (ImageAttributes ia = new ImageAttributes())
            {
                ia.SetColorMatrix(cm);

                using (Graphics g = Graphics.FromImage(result))
                {
                    g.DrawImage(original, 
                        new Rectangle(0, 0, original.Width, original.Height),
                        0, 0, 
                        original.Width, 
                        original.Height,
                        GraphicsUnit.Pixel, 
                        ia);
                }
            }

            return result;
        }
    }
}

我需要输出在两种情况下具有相同数量的颜色。任何帮助将不胜感激。谢谢!

如果我最初的帖子令人困惑,我很抱歉。另一个编辑:在此处发布之前,我忘记回滚一些编辑。我现在正在使用另一个位图构造函数。
Bitmap result = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
场景和结果也被编辑。

标签: c#winformsbitmapgdi+

解决方案


回答我自己的问题。那么为什么保存半透明位图后颜色的数量会发生变化呢?

看起来像类Graphics虽然快速且易于使用,但在处理半透明图像时实际上并不那么精确。我设法通过使用GetPixel()SetPixel()方法获得了预期的结果。由于性能低下、缺乏灵活性等明显的原因,我避免使用它们。但它们完成了工作。

        private static Image GetNewImage(string path)
        {
            Bitmap original = new Bitmap(path);
            Bitmap result = new Bitmap(original.Width, original.Height);

            for (int x = 0; x < original.Width; x++)
            {
                for (int y = 0; y < original.Height; y++)
                {
                    var px = original.GetPixel(x, y);
                    result.SetPixel(x, y, Color.FromArgb((int)GetColorLuminance(px), px.R, px.G, px.B));
                }
            }

            return result;
        }

        private static float GetColorLuminance(Color color)
        {
            var R = color.R * .2125f;
            var G = color.G * .7154f;
            var B = color.B * .0721f;

            return R + G + B;
        }

推荐阅读