1

我正在尝试将 3 个灰度位图组合成一个颜色位图。所有三个灰度图像的大小相同(这是基于来自哈勃的数据)。我的逻辑是:加载“蓝色”图像并转换为PixelFormat.Format24bppRgb. 基于此,创建一个新的字节数组,其大小是蓝色数据数组长度/3 的 4 倍(因此它将是一个蓝色字节,一个绿色字节,一个红色字节,一个字节每个像素的 alpha,因为我的系统是小端)。从蓝色图像的“蓝色”字节填充数组的“蓝色”字节(在第一个循环中,将 alpha 字节设置为 255)。然后我加载绿色和红色位图,将它们转换为PixelFormat.Format24bppRgb,然后提取 g/r 值并将其添加到数据数组中的正确位置。然后,根据我的判断,最终的数据数组正确设置了 bgra 字节。

当我填充了数据数组时,我用它来:

创建一个PixelFormats.Bgra32 BitmapSource然后将其转换为位图。

PixelFormat.Format32bppArgb使用 Bitmap 构造函数(宽度、高度、步幅、PixelForma、IntPtr)创建位图

PixelFormat.Format32bppArgb使用指针创建位图

创建返回位图的所有三种方式都会导致图像“倾斜”(抱歉,我不知道更好的词)。

实际输出(生成最终位图的所有三种方式)是:实际输出

所需的输出类似于(这是在 Photoshop 中完成的,因此略有不同):所需的输出

三个文件名 ( _blueFileName, _greenFileName, _redFileName) 在构造函数中设置,我在创建类之前检查以确保文件存在。如果有人想要,我可以发布该代码。

谁能告诉我我做错了什么?我猜这是由于步幅或类似的原因?

注意:我无法发布指向我用作输入的图像的链接,因为我没有 10 个声望点。如果有人也想要它们,也许我可以通过电子邮件或其他方式发送链接。

这是我的代码(注释掉了一些东西,注释描述了如果使用每个注释掉的块会发生什么):

    public Bitmap Merge()
    {
        //  Load original "blue" bitmap.
        Bitmap tblueBitmap = (Bitmap)Image.FromFile(_blueFileName);
        int width = tblueBitmap.Width;
        int height = tblueBitmap.Height;
        //  Convert to 24 bpp rgb (which is bgr on little endian machines)
        Bitmap blueBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        using (Graphics gr = Graphics.FromImage(blueBitmap))
        {
            gr.DrawImage(tblueBitmap, 0, 0, width, height);
        }
        tblueBitmap.Dispose();
        //  Lock and copy to byte array.
        BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly,
            blueBitmap.PixelFormat);
        int numbBytes = blueData.Stride*blueBitmap.Height;
        byte[] blueBytes = new byte[numbBytes];
        Marshal.Copy(blueData.Scan0, blueBytes, 0, numbBytes);
        blueBitmap.UnlockBits(blueData);
        blueData = null;
        blueBitmap.Dispose();
        int mult = 4;
        byte[] data = new byte[(numbBytes/3)*mult];
        int count = 0;
        //  Copy every third byte starting at 0 to the final data array (data).
        for (int i = 0; i < data.Length / mult; i++)
        {
            //  Check for overflow
            if (blueBytes.Length <= count*3 + 2)
            {
                continue;
            }
            //  First pass, set Alpha channel.
            data[i * mult + 3] = 255;
            //  Set blue byte.
            data[i*mult] = blueBytes[count*3];
            count++;
        }
        //  Cleanup.
        blueBytes = null;
        int generation = GC.GetGeneration(this);
        GC.Collect(generation);

        Bitmap tgreenBitmap = (Bitmap)Image.FromFile(_greenFileName);
        Bitmap greenBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        using (Graphics gr = Graphics.FromImage(greenBitmap))
        {
            gr.DrawImage(tgreenBitmap, 0, 0, width, height);
        }
        tgreenBitmap.Dispose();
        BitmapData greenData = greenBitmap.LockBits(new Rectangle(0, 0, greenBitmap.Width, greenBitmap.Height), ImageLockMode.ReadOnly,
            greenBitmap.PixelFormat);
        numbBytes = greenData.Stride * greenBitmap.Height;
        byte[] greenBytes = new byte[numbBytes];
        Marshal.Copy(greenData.Scan0, greenBytes, 0, numbBytes);
        greenBitmap.UnlockBits(greenData);
        greenData = null;
        greenBitmap.Dispose();
        count = 0;
        for (int i = 0; i < data.Length / mult; i++)
        {
            if (greenBytes.Length <= count * 3 + 1)
            {
                continue;
            }
            //  Set green byte
            data[i * mult + 1] = greenBytes[count * 3 + 1];
            count++;
        }
        greenBytes = null;
        generation = GC.GetGeneration(this);
        GC.Collect(generation);

        Bitmap tredBitmap = (Bitmap)Image.FromFile(_redFileName);
        Bitmap redBitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        using (Graphics gr = Graphics.FromImage(redBitmap))
        {
            gr.DrawImage(tredBitmap, 0, 0, width, height);
        }
        tredBitmap.Dispose();
        BitmapData redData = redBitmap.LockBits(new Rectangle(0, 0, redBitmap.Width, redBitmap.Height), ImageLockMode.ReadOnly,
            redBitmap.PixelFormat);
        numbBytes = redData.Stride * redBitmap.Height;
        byte[] redBytes = new byte[numbBytes];
        Marshal.Copy(redData.Scan0, redBytes, 0, numbBytes);
        redBitmap.UnlockBits(redData);
        redData = null;
        redBitmap.Dispose();
        count = 0;
        for (int i = 0; i < data.Length / mult; i++)
        {
            if (redBytes.Length <= count * 3+2)
            {
                count++;
                continue;
            }
            // set red byte
            data[i * mult + 2] = redBytes[count * 3 + 2];
            count++;
        }
        redBytes = null;
        generation = GC.GetGeneration(this);
        GC.Collect(generation);
        int stride = (width*32 + 7)/8;
        var bi = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgra32, null, data, stride);
        //  uncomment out below to see what a bitmap source to bitmap does.  So far, it is exactly the same as 
        //  the uncommented out lines below.
        //  ---------------------------------------------------------------------------------------------------
        //return BitmapImage2Bitmap(bi);
        unsafe
        {
            fixed (byte* p = data)
            {
                IntPtr ptr = (IntPtr)p;
                //  Trying the commented out lines returns the same bitmap as the uncommented out lines.
                //  ------------------------------------------------------------------------------------
                byte* p2 = (byte*)ptr;
                Bitmap retBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                BitmapData fData = retBitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
                    PixelFormat.Format32bppArgb);
                unsafe
                {
                    for (int i = 0; i < fData.Height; i++)
                    {
                        byte* imgPtr = (byte*)(fData.Scan0 + (fData.Stride * i));
                        for (int x = 0; x < fData.Width; x++)
                        {
                            for (int ii = 0; ii < 4; ii++)
                            {
                                *imgPtr++ = *p2++;
                            }
                            //*imgPtr++ = 255;
                        }
                    }
                }
                retBitmap.UnlockBits(fData);
                //Bitmap retBitmap = new Bitmap(width, height, GetStride(width, PixelFormat.Format32bppArgb),
                //    PixelFormat.Format32bppArgb, ptr);
                return retBitmap;
            }
        }

    }

    private Bitmap BitmapImage2Bitmap(BitmapSource bitmapSrc)
    {
        using (MemoryStream outStream = new MemoryStream())
        {
            BitmapEncoder enc = new BmpBitmapEncoder();
            enc.Frames.Add(BitmapFrame.Create(bitmapSrc));
            enc.Save(outStream);
            Bitmap bitmap = new Bitmap(outStream);
            return new Bitmap(bitmap);
        }
    }

    private int GetStride(int width, PixelFormat pxFormat)
    {
        int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
        int validBitsPerLine = width * bitsPerPixel;
        int stride = ((validBitsPerLine + 31) / 32) * 4;
        return stride;
    }
4

1 回答 1

3

你错过了线条之间的差距。该Stride值不是一行中的数据量,而是一行开始到下一行的距离。每行的末尾可能有一个间隙,以便在偶数地址边界上对齐下一行​​。

Stride值甚至可以是负数,然后图像倒置存储在内存中。要获得没有间隙的数据并处理所有情况,您需要一次复制一行:

BitmapData blueData = blueBitmap.LockBits(new Rectangle(0, 0, blueBitmap.Width, blueBitmap.Height), ImageLockMode.ReadOnly, blueBitmap.PixelFormat);
int lineBytes = blueBitmap.Width * 3;
int numbBytes = lineBytes * blueBitmap.Height;
byte[] blueBytes = new byte[numbBytes];
for (int y = 0; y < blueBitmap.Height; y++) {
  Marshal.Copy(blueData.Scan0 + y * blueData.Stride, blueBytes, y * lineBytes, lineBytes);
}
blueBitmap.UnlockBits(blueData);
blueBitmap.Dispose();
于 2014-06-13T09:19:25.217 回答