You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TIFF图片切片转存PNG/JPG失败:Image.FromStream报错参数无效

问题分析与解决方案

你的代码在TIFF切片保存时正常工作,但转JPG/PNG时出现"参数无效"错误,核心原因是:Image.FromStream需要读取完整的图像格式数据(带文件头、像素格式描述等),而你传入的是未经编码的原始像素字节流,系统无法识别。

TIFF保存逻辑之所以可行,是因为你手动设置了TIFF格式所需的所有元数据(宽度、高度、像素格式等),而JPG/PNG的Image.FromStream没有这些上下文信息,无法解析原始像素。


修正后的代码(DO_JPEG块替换)

将原代码中#if DO_JPEG到#endif的块替换为以下内容,通过创建Bitmap对象直接操作像素数据,再导出为JPG/PNG:

#if DO_JPEG
using (Bitmap bitmap = CreateBitmapFromRawPixels(W, H, samplesPerPixel, bitsPerSample, photo, output, nz))
{
    // 根据文件名后缀自动选择保存格式
    ImageFormat format = TileName.EndsWith(".png", StringComparison.OrdinalIgnoreCase) 
        ? ImageFormat.Png 
        : ImageFormat.Jpeg;
    bitmap.Save(TileName, format);
}
#endif

// 新增辅助方法:从原始像素创建Bitmap
private static Bitmap CreateBitmapFromRawPixels(int width, int height, int samplesPerPixel, int bitsPerSample, int photo, byte[] rawPixels, int rowByteSize)
{
    PixelFormat pixelFormat = GetPixelFormat(samplesPerPixel, bitsPerSample, photo);
    Bitmap bitmap = new Bitmap(width, height, pixelFormat);

    // 灰度图需要手动设置调色板
    if (pixelFormat == PixelFormat.Format8bppIndexed)
    {
        ColorPalette palette = bitmap.Palette;
        for (int i = 0; i < 256; i++)
        {
            palette.Entries[i] = Color.FromArgb(i, i, i);
        }
        bitmap.Palette = palette;
    }

    // 锁定Bitmap内存,直接写入像素数据
    BitmapData bmpData = bitmap.LockBits(
        new Rectangle(0, 0, width, height), 
        ImageLockMode.WriteOnly, 
        pixelFormat
    );

    try
    {
        IntPtr bufferPtr = bmpData.Scan0;
        int stride = bmpData.Stride;

        for (int row = 0; row < height; row++)
        {
            int rawRowOffset = row * rowByteSize;
            int bmpRowOffset = row * stride;

            // 根据像素格式处理字节顺序(如RGB转BGR)
            if (pixelFormat == PixelFormat.Format24bppRgb)
            {
                // TIFF的RGB顺序转为Bitmap的BGR顺序
                for (int col = 0; col < rowByteSize; col += 3)
                {
                    byte r = rawPixels[rawRowOffset + col];
                    byte g = rawPixels[rawRowOffset + col + 1];
                    byte b = rawPixels[rawRowOffset + col + 2];

                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col, b);
                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col + 1, g);
                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col + 2, r);
                }
            }
            else if (pixelFormat == PixelFormat.Format32bppArgb)
            {
                // TIFF的RGBA顺序转为Bitmap的BGRA顺序
                for (int col = 0; col < rowByteSize; col += 4)
                {
                    byte r = rawPixels[rawRowOffset + col];
                    byte g = rawPixels[rawRowOffset + col + 1];
                    byte b = rawPixels[rawRowOffset + col + 2];
                    byte a = rawPixels[rawRowOffset + col + 3];

                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col, b);
                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col + 1, g);
                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col + 2, r);
                    Marshal.WriteByte(bufferPtr, bmpRowOffset + col + 3, a);
                }
            }
            else
            {
                // 灰度图等无需字节顺序转换的格式,直接复制整行
                Marshal.Copy(rawPixels, rawRowOffset, bufferPtr + bmpRowOffset, rowByteSize);
            }
        }
    }
    finally
    {
        bitmap.UnlockBits(bmpData);
    }

    return bitmap;
}

// 新增辅助方法:映射TIFF像素格式到Bitmap像素格式
private static PixelFormat GetPixelFormat(int samplesPerPixel, int bitsPerSample, int photo)
{
    if (samplesPerPixel == 1 && bitsPerSample == 8 && photo == (int)TiffTag.PHOTOMETRIC.MINISBLACK)
    {
        return PixelFormat.Format8bppIndexed;
    }
    else if (samplesPerPixel == 3 && bitsPerSample == 8 && photo == (int)TiffTag.PHOTOMETRIC.RGB)
    {
        return PixelFormat.Format24bppRgb;
    }
    else if (samplesPerPixel == 4 && bitsPerSample == 8 && photo == (int)TiffTag.PHOTOMETRIC.RGB)
    {
        return PixelFormat.Format32bppArgb;
    }
    else
    {
        throw new NotSupportedException($"不支持的像素格式:samplesPerPixel={samplesPerPixel}, bitsPerSample={bitsPerSample}");
    }
}

关键修复点说明

  1. 像素格式映射:根据TIFF的元数据(采样数、位深、光度解释)匹配对应的PixelFormat,确保Bitmap能正确解析原始像素。
  2. 字节顺序转换:Bitmap的24/32位格式采用BGR/BGRA字节顺序,而TIFF通常使用RGB/RGBA,必须手动交换通道顺序。
  3. Stride处理:Bitmap的行字节数(Stride)会自动对齐到4字节边界,需逐行复制像素而非直接拷贝整个数组。
  4. 灰度图调色板:8位索引灰度图需要手动生成灰度调色板,否则会显示异常颜色。

内容的提问来源于stack exchange,提问作者gpol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 11:25:18