为什么保存位图后颜色数量会发生变化?
Why does the number of colors change after saving a bitmap?
我正在创建一个单一用途的应用程序,它可以拍摄图像并通过一些 Alpha 通道调整来保存它。这些调整是通过颜色矩阵进行的,旨在仅改变 alpha 通道。我希望输出在 RGB 值方面与原始输出相同。
我正在处理使用 Photoshop 或 After Effects 创建的 32 位 PNG 图像。我 save/render 文件没有任何压缩(至少有人告诉我)。
这是两个颜色矩阵,可以帮助我说明问题。
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
对颜色数量没有任何影响。
应用 luminanceToAlphaCm
导致颜色数量减少:1052.
场景#2。文件:具有多个半透明区域的 32 位 PNG(相同图像但没有背景),颜色数 6244.
应用 harmlessCm
导致颜色数量减少:5990。
应用 luminanceToAlphaCm
导致颜色数量减少:1470。
完整 class:
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;
}
}
}
我需要输出在两种情况下具有相同数量的颜色。
任何帮助将不胜感激。
谢谢!
如果我最初的 post 令人困惑,我深表歉意。
另一个编辑:我忘记在 post 到这里之前回滚一些编辑。我现在正在使用另一个位图构造函数。
Bitmap result = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
场景和结果也进行了编辑。
有两种发布图像的方式:未压缩和通过有损过程压缩。 PNG 和JPG 是后者。* 基本思想与MP3 相同。没有人真正解压缩。
(*还有一些其他的,比如 SVG,但是这可能对你没有帮助。)
如果我截取桌面屏幕截图(2560x1440,64 色深),像素为 3,686,400。每个像素 64 位是每个像素 8 个字节。所以这张图片的时钟大小约为 29,491,200 字节或 29 兆字节。没有人愿意在互联网上移动或存储这些东西。
所以像 .jpg、.png 和 .mp3 这样的压缩只从源中选择 select 几个值,并做一些涉及高端人类 color/sound 解析科学的真正高级数学以减少文件大小并且希望没有 人类可察觉的 质量损失。这就是调色板如何得到这个既不是 16、32 也不是 64 位的奇数。
由于这些原因,.PNG、.JPG 和 .MP3 等格式非常适合在 Internet 上发布图像(包括邮件和网页)。不幸的是,这也使它们对于进一步的图像处理非常不利,而这正是您所做的。您应该始终对 bitmap/uncompressed 图像进行操作。如果它是来自数码相机的图像,甚至可能有数据的预处理原始数据。
最后警告:在运输或存储过程中如何压缩并不重要。一旦加载它进行处理或显示,所有压缩步骤都必须撤消。你最终得到的更接近你开始的位图,减去一些压缩伪影。
回答我自己的问题。那么为什么保存半透明位图后颜色数量会发生变化?
看起来像 class 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;
}
我正在创建一个单一用途的应用程序,它可以拍摄图像并通过一些 Alpha 通道调整来保存它。这些调整是通过颜色矩阵进行的,旨在仅改变 alpha 通道。我希望输出在 RGB 值方面与原始输出相同。
我正在处理使用 Photoshop 或 After Effects 创建的 32 位 PNG 图像。我 save/render 文件没有任何压缩(至少有人告诉我)。
这是两个颜色矩阵,可以帮助我说明问题。
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
对颜色数量没有任何影响。
应用 luminanceToAlphaCm
导致颜色数量减少:1052.
场景#2。文件:具有多个半透明区域的 32 位 PNG(相同图像但没有背景),颜色数 6244.
应用 harmlessCm
导致颜色数量减少:5990。
应用 luminanceToAlphaCm
导致颜色数量减少:1470。
完整 class:
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;
}
}
}
我需要输出在两种情况下具有相同数量的颜色。 任何帮助将不胜感激。 谢谢!
如果我最初的 post 令人困惑,我深表歉意。
另一个编辑:我忘记在 post 到这里之前回滚一些编辑。我现在正在使用另一个位图构造函数。
Bitmap result = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
场景和结果也进行了编辑。
有两种发布图像的方式:未压缩和通过有损过程压缩。 PNG 和JPG 是后者。* 基本思想与MP3 相同。没有人真正解压缩。
(*还有一些其他的,比如 SVG,但是这可能对你没有帮助。)
如果我截取桌面屏幕截图(2560x1440,64 色深),像素为 3,686,400。每个像素 64 位是每个像素 8 个字节。所以这张图片的时钟大小约为 29,491,200 字节或 29 兆字节。没有人愿意在互联网上移动或存储这些东西。
所以像 .jpg、.png 和 .mp3 这样的压缩只从源中选择 select 几个值,并做一些涉及高端人类 color/sound 解析科学的真正高级数学以减少文件大小并且希望没有 人类可察觉的 质量损失。这就是调色板如何得到这个既不是 16、32 也不是 64 位的奇数。
由于这些原因,.PNG、.JPG 和 .MP3 等格式非常适合在 Internet 上发布图像(包括邮件和网页)。不幸的是,这也使它们对于进一步的图像处理非常不利,而这正是您所做的。您应该始终对 bitmap/uncompressed 图像进行操作。如果它是来自数码相机的图像,甚至可能有数据的预处理原始数据。
最后警告:在运输或存储过程中如何压缩并不重要。一旦加载它进行处理或显示,所有压缩步骤都必须撤消。你最终得到的更接近你开始的位图,减去一些压缩伪影。
回答我自己的问题。那么为什么保存半透明位图后颜色数量会发生变化?
看起来像 class 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;
}