我不知道 TIFF 格式可以同时拥有两种不同的色彩空间。由于您是在处理 CMYK,我认为这是您想要保留的。
如果是这样,这样做的步骤是:
- 加载 CMYK 图像 A(使用
BitmapDecoder)
- 加载 RGB 图像 B(使用
BitmapDecoder)
- 将图像 B 转换为具有所需颜色配置文件的 CMYK(使用
FormatConvertedBitmap)
- 如果需要,确保图像 B 的像素格式与 A 匹配(使用
FormatConvertedBitmap)
- 在内存中将两者合成为字节数组(使用
CopyPixels,然后是内存操作,然后是内存中的新位图)
- 将合成保存到新的 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:
示例输出: