c#创建PixelFormat.Format32bppArgb倾斜图像

本文关键字:倾斜 图像 Format32bppArgb PixelFormat 创建 | 更新日期: 2023-09-27 18:08:22

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

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

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

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

创建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;
    }

c#创建PixelFormat.Format32bppArgb倾斜图像

你错过了行与行之间的间隙。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();