【问题标题】:Concatenate a bitmap (rgb) with a TIFF (cmyk) without converting cmyk to rgb将位图 (rgb) 与 TIFF (cmyk) 连接,而不将 cmyk 转换为 rgb
【发布时间】:2021-03-15 19:15:26
【问题描述】:

我正在开发一个应用程序来连接 RGB 中的位图图像和 CMYK 中的 TIFF。 我尝试过使用 System.Drawing 和 System.Windows.Media 命名空间。

问题是两个库都试图在合并之前将我的 TIFF 图像转换为 RGB,这会导致图像质量下降

据我了解,他们总是在处理之前将图像转换为 RGB 的原因是因为这两个库是出于渲染意图这样做的。

我不需要渲染任何东西,只需将两张照片合并并保存到磁盘,仅此而已。

我应该怎么做才能实现我的目标?显然,我不想失去 TIFF 的质量,所以我认为最好不要进行任何转换,保持原始并合并。无论如何,这只是一个猜测,其他选项也可以考虑。请问有人能解释一下我的情况吗?

请看下面从 cmyk 转换为 rgb 前后的 tiff 图像对比。

【问题讨论】:

  • 这里的“合并”是什么意思?以某种方式将它们放在一起?
  • 是的,以某种方式将它们放在一起。例如:两张图片高度相同,左1右1,另存为一张;或者它们具有相同的宽度,将它们放在彼此的顶部以对齐左右边缘,然后将它们保存为一个图像
  • 请提供一些代码来显式连接以及您如何使用 Daring 和 Media。从您提供的图像中,我没有看到质量下降,只是色彩空间发生了变化。即使您以每通道 8 位处理所有内容,也不应该有任何质量损失。
  • @Soleil-MathieuPrévot 我可以和你聊聊这个吗?

标签: c# rgb tiff cmyk


【解决方案1】:

我不知道 TIFF 格式可以同时拥有两种不同的色彩空间。由于您是在处理 CMYK,我认为这是您想要保留的。

如果是这样,这样做的步骤是:

  1. 加载 CMYK 图像 A(使用 BitmapDecoder
  2. 加载 RGB 图像 B(使用 BitmapDecoder
  3. 将图像 B 转换为具有所需颜色配置文件的 CMYK(使用 FormatConvertedBitmap
  4. 如果需要,确保图像 B 的像素格式与 A 匹配(使用 FormatConvertedBitmap
  5. 在内存中将两者合成为字节数组(使用CopyPixels,然后是内存操作,然后是内存中的新位图)
  6. 将合成保存到新的 CMYK TIFF 文件(使用 TiffBitmapEncoder

WIC (System.Media) 应该可以做到这一点。

这样做的一个例子 (github) 可以写成:

BitmapFrame LoadTiff(string filename)
{
    using (var rs = File.OpenRead(filename))
    {
        return BitmapDecoder.Create(rs, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad).Frames[0];
    }
}

// Load, validate A
var imageA = LoadTiff("CMYK.tif");
if (imageA.Format != PixelFormats.Cmyk32)
{
    throw new InvalidOperationException("imageA is not CMYK");
}

// Load, validate, convert B
var imageB = LoadTiff("RGB.tif");
if (imageB.PixelHeight != imageA.PixelHeight)
{
    throw new InvalidOperationException("Image B is not the same height as image A");
}
var imageBCmyk = new FormatConvertedBitmap(imageB, imageA.Format, null, 0d);

// Merge
int width = imageA.PixelWidth + imageB.PixelWidth,
    height = imageA.PixelHeight,
    bytesPerPixel = imageA.Format.BitsPerPixel / 8,
    stride = width * bytesPerPixel;
var buffer = new byte[stride * height];
imageA.CopyPixels(buffer, stride, 0);
imageBCmyk.CopyPixels(buffer, stride, imageA.PixelWidth * bytesPerPixel);
var result = BitmapSource.Create(width, height, imageA.DpiX, imageA.DpiY, imageA.Format, null, buffer, stride);

// save to new file
using (var ws = File.Create("out.tif"))
{
    var tiffEncoder = new TiffBitmapEncoder();
    tiffEncoder.Frames.Add(BitmapFrame.Create(result));
    tiffEncoder.Save(ws);
}

它保持 CMYK 图像的颜色准确性,并使用系统颜色配置文件转换 RGB。这可以在 Photoshop 中进行验证,显示每个字母和浓郁的黑色都保持了它们的原始值。 (请注意,imgur 确实转换为具有可疑颜色处理的 png - check github for originals。)

图像 A (CMYK):

图像 B (RGB):

结果(CMYK):

要使两张图像重叠,一张图像必须具有某种透明度的概念。遮罩就是其中的一个示例,您可以在其中选择特定的颜色值来表示“透明”。遮罩的缺点是遮罩不能很好地处理混叠的源图像。为此,您会想要做一个 Alpha 通道 - 但跨色彩空间混合将具有挑战性。 (Github)

// Load, validate A
var imageA = LoadTiff("CMYK.tif");
if (imageA.Format != PixelFormats.Cmyk32)
{
    throw new InvalidOperationException("imageA is not CMYK");
}

// Load, validate, convert B
var imageB = LoadTiff("RGBOverlay.tif");
if (imageB.PixelHeight != imageA.PixelHeight
    || imageB.PixelWidth != imageA.PixelWidth)
{
    throw new InvalidOperationException("Image B is not the same size as image A");
}
var imageBBGRA = new FormatConvertedBitmap(imageB, PixelFormats.Bgra32, null, 0d);
var imageBCmyk = new FormatConvertedBitmap(imageB, imageA.Format, null, 0d);

// Merge
int width = imageA.PixelWidth, height = imageA.PixelHeight;
var stride = width * (imageA.Format.BitsPerPixel / 8);
var bufferA = new uint[width * height];
var bufferB = new uint[width * height];
var maskBuffer = new uint[width * height];
imageA.CopyPixels(bufferA, stride, 0);
imageBBGRA.CopyPixels(maskBuffer, stride, 0);
imageBCmyk.CopyPixels(bufferB, stride, 0);

for (int i = 0; i < bufferA.Length; i++)
{
    // set pixel in bufferA to the value from bufferB if mask is not white
    if (maskBuffer[i] != 0xffffffff)
    {
        bufferA[i] = bufferB[i];
    }
}

var result = BitmapSource.Create(width, height, imageA.DpiX, imageA.DpiY, imageA.Format, null, bufferA, stride);

// save to new file
using (var ws = File.Create("out_overlay.tif"))
{
    var tiffEncoder = new TiffBitmapEncoder();
    tiffEncoder.Frames.Add(BitmapFrame.Create(result));
    tiffEncoder.Save(ws);
}

示例图片 B:

示例输出:

【讨论】:

  • 您能否更详细地解释第 5 步?我尝试使用 RenderingTargetBitmap 没有成功,不知道那里的渲染......
  • @HoangDucNguyen,我添加了一些示例代码。 RenderTargetBitmap 是 WPF 的一部分 - 不是 WIC,因此据我所知仅支持 RGB。我确实说错了。我的意思是CopyPixels - 不是Render
  • 我正在尝试将 imageA 放在图像 B 的顶部(现在假设图像 B 的宽度与图像 A 相同),但我有点迷路了。它是我应该使用的偏移参数吗?我试过了,但结果看起来很奇怪。我试图了解 copypixels 如何使用其参数
  • 您可以直接使用CopyPixels 做的唯一事情是将它们彼此相邻放置。如果您想将它们叠加在一起,则必须以某种方式bitblt。顶部图像是否以某种方式被遮盖?
  • 我不明白什么是蒙面。但我只是将 imageB 调整为与图像 A 具有相同的宽度,然后想将 A 放在 B 的顶部,或者 B 放在 A 的顶部。你能帮忙提供一个示例代码吗?
猜你喜欢
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 2020-07-03
  • 2016-01-11
  • 2020-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多