C#创建PixelFormat.Format32bppArgb偏斜图像

时间:2014-06-13 08:57:53

标签: c# bitmap bitmapsource

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

当我填充数据数组时,我已将其用于:

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

使用Bitmap构造函数创建PixelFormat.Format32bppArgb位图(width,height,stride,PixelForma,IntPtr)

使用指针

创建PixelFormat.Format32bppArgb位图

创建返回位图的所有三种方法都会导致图像被"倾斜" (对不起,我不知道一个更好的词)。

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

所需的输出就像(这在photoshop中完成,因此略有不同):Desired output

在构造函数中设置了三个文件名(_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;
    }

1 个答案:

答案 0 :(得分: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();