在不将 cmyk 转换为 rgb 的情况下将位图 (rgb) 与 TIFF (cmyk) 连接起来

Concatenate a bitmap (rgb) with a TIFF (cmyk) without converting cmyk to rgb

我正在开发一个应用程序,用于将 RGB 格式的位图图像与 CMYK 格式的 TIFF 图像连接起来。 我试过 System.Drawing 和 System.Windows.Media 命名空间。

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

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

我不需要渲染任何东西,只需将两张照片合并并保存到磁盘,就这样。

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

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

我不知道 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:

示例输出: