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

基于Bitmap生成int[,]数组:ASCOM驱动DNG转图像阵列异常

问题分析与修复步骤

1. ReadBitmap函数:修复Stride处理错误

当前代码忽略了Bitmap的Stride属性(每行实际占用的字节数,必须是4的倍数),直接按总像素索引*3读取字节,会误读到行尾的对齐padding数据,导致像素错位,这是图像异常的核心原因。

修复后的代码:

public int[,] ReadBitmap(Bitmap img)
{
    BitmapData data = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadOnly, img.PixelFormat);
    IntPtr ptr = data.Scan0;
    int stride = Math.Abs(data.Stride);
    var result = new int[img.Width, img.Height];

    byte[] bytesArray = new byte[stride * img.Height];
    Marshal.Copy(ptr, bytesArray, 0, bytesArray.Length);
    img.UnlockBits(data);

    int width = img.Width;
    int height = img.Height;

    Parallel.For(0, height, row =>
    {
        int rowStart = row * stride;
        for (int col = 0; col < width; col++)
        {
            int pixelOffset = rowStart + col * 3;
            byte b = bytesArray[pixelOffset];
            byte g = bytesArray[pixelOffset + 1];
            byte r = bytesArray[pixelOffset + 2];

            result[col, row] = (int)(0.299 * r + 0.587 * g + 0.114 * b);
        }
    });

    return result;
}

关键修改:

  • 按行处理数据,每行起始位置为row * stride
  • 每个像素的偏移计算为行起始+列索引*3,彻底避开行尾padding
  • 改用行循环+列循环的结构,逻辑更直观且避免Stride冲突

2. DNG转Bitmap步骤:修复颜色通道与内存复制问题

现存问题:

  • libraw默认输出RGB顺序的像素数据,但Format24bppRgb格式的Bitmap实际存储为BGR顺序,直接复制会导致颜色反转
  • 手动拼接padding后,内存复制长度错误,仅复制了原始数据,未包含对齐padding字节

修复后的代码片段:

// ... 保留libraw初始化与处理的前序代码 ...

// 计算Bitmap要求的4字节对齐Stride
int bitmapStride = ((img.width * 3) + 3) & ~3;
int totalBytes = bitmapStride * img.height;
byte[] bitmapData = new byte[totalBytes];

for (int i = 0; i < img.height; i++)
{
    int srcRowStart = i * img.width * 3;
    int dstRowStart = i * bitmapStride;

    // 将RGB转换为Bitmap要求的BGR顺序
    for (int j = 0; j < img.width; j++)
    {
        byte r = img.data[srcRowStart + j * 3];
        byte g = img.data[srcRowStart + j * 3 + 1];
        byte b = img.data[srcRowStart + j * 3 + 2];

        bitmapData[dstRowStart + j * 3] = b;
        bitmapData[dstRowStart + j * 3 + 1] = g;
        bitmapData[dstRowStart + j * 3 + 2] = r;
    }
}

// 释放libraw内存
NativeMethods.libraw_dcraw_clear_mem(ptr);

// 创建Bitmap并写入数据
var bmp = new Bitmap(img.width, img.height, PixelFormat.Format24bppRgb);
var bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
Marshal.Copy(bitmapData, 0, bmd.Scan0, totalBytes);
bmp.UnlockBits(bmd);

NativeMethods.libraw_close(data);

// 保存JPEG
var outJPEG = path.Replace(Path.GetExtension(path), ".jpg");
bmp.Save(outJPEG, ImageFormat.Jpeg);
CurrentFilePath = outJPEG;

关键修改:

  • 显式转换RGB→BGR通道顺序,解决颜色反转问题
  • 自动计算Bitmap的对齐Stride,避免手动拼接padding的繁琐操作
  • 使用Marshal.Copy完整复制包含padding的Bitmap数据,确保图像完整性

3. 额外优化:跳过JPEG中间步骤

当前DNG→Bitmap→JPEG→Bitmap→int[,]的流程存在冗余,JPEG压缩会损失画质且增加开销,建议直接从libraw处理后的内存数据生成ASCOM图像阵列:

public int[,] ConvertLibrawDataToImageArray(libraw_processed_image_t img)
{
    int width = img.width;
    int height = img.height;
    int[,] result = new int[width, height];

    Parallel.For(0, height, row =>
    {
        int rowStart = row * width * 3;
        for (int col = 0; col < width; col++)
        {
            int pixelOffset = rowStart + col * 3;
            byte r = img.data[pixelOffset];
            byte g = img.data[pixelOffset + 1];
            byte b = img.data[pixelOffset + 2];

            result[col, row] = (int)(0.299 * r + 0.587 * g + 0.114 * b);
        }
    });

    return result;
}

此方式可直接跳过JPEG转换环节,提升效率并避免画质损失。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:39:27