【问题标题】:Viewing a large bitmap image using memory mapped view使用内存映射视图查看大位图图像
【发布时间】:2015-04-03 08:07:07
【问题描述】:

我正在尝试使用 C# 应用程序查看一个非常大的位图。

这里的主要问题是我无法将它直接加载到内存中,因此我尝试使用内存映射视图库来使用来自Reference OneReference TwoReference ThreeReference Four 和 @987654325 的指导来加载它@ .

到目前为止,我达到的内容如下:- 根据需要分部分读取位图(例如,我读取了前 200 行)。 从我读取的行创建另一个位图并显示它。

问题:- 重建的位图图像部分丢失了颜色信息,并被上下颠倒显示。

示例:- [注意我在这里使用小尺寸图像并尝试显示其中的一部分以用于测试目的]

真实形象:-

应该是输出(选择前 200 行并重建一个较小的位图并显示它):- 如您所见,重建后的图像是无色且上下颠倒的。

现在是代码部分:- 负责整个过程的BMPMMF类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace BMPViewer
{
    class BMPMMF
    {
        /// <summary>
        /// It opens the image using memory mapped view and read the needed 
        /// parts, then call CreateBM to create a partially bitmap
        /// </summary>
        /// <param name="bmpFilename">Path to the physical bitmap</param>
        /// <returns></returns>
        public Bitmap readPartOfImage(string bmpFilename)
        {
            var headers = ReadHeaders(bmpFilename);
            var mmf = MemoryMappedFile.CreateFromFile(bmpFilename, FileMode.Open);
            int rowSize = headers.Item2.RowSize;    // number of byes in a row

           // Dictionary<ColorObject, int> rowColors = new Dictionary<ColorObject, int>();

            int colorSize = Marshal.SizeOf(typeof(MyColor));
            int width = rowSize / colorSize;//(headers.Item1.DataOffset+ rowSize) / colorSize;
            int height = 200;
            ColorObject cObj;
            MyColor outObj;
            ColorObject[][] rowColors = new ColorObject[height][];
            // Read the view image and save row by row pixel
            for (int j = 0; j < height; j++)
            {
                rowColors[j] = new ColorObject[width];
                using (var view = mmf.CreateViewAccessor(headers.Item1.DataOffset + rowSize * j, rowSize, MemoryMappedFileAccess.Read))
                {
                    for (long i = 0; i < rowSize; i += colorSize)
                    {

                        view.Read(i, out outObj);
                        cObj = new ColorObject(outObj);
                        rowColors[j][i / colorSize] = cObj;


                    }
                }
            }
            return CreateBM( rowColors );

        }
        /// <summary>
        /// Used to create a bitmap from provieded bytes 
        /// </summary>
        /// <param name="rowColors">Contains bytes of bitmap</param>
        /// <returns></returns>
        private Bitmap CreateBM(ColorObject[][] rowColors )
        {
            int width = rowColors[0].Count();
            int height = rowColors.Count();
            //int width = rowColors.Values.Where(o => o == 0).Count();
            Bitmap bitm = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            // new Bitmap(imgdat.GetUpperBound(1) + 1, imgdat.GetUpperBound(0) + 1, PixelFormat.Format24bppRgb);
            BitmapData bitmapdat = bitm.LockBits(new Rectangle(0, 0, bitm.Width, bitm.Height), ImageLockMode.ReadWrite, bitm.PixelFormat);
            int stride = bitmapdat.Stride;
            byte[] bytes = new byte[stride * bitm.Height];

            for (int r = 0; r < bitm.Height; r++)
            {

                for (int c = 0; c < bitm.Width; c++)
                {
                    ColorObject color = rowColors[r][c];
                    bytes[(r * stride) + c * 3] = color.Blue;
                    bytes[(r * stride) + c * 3 + 1] = color.Green;
                    bytes[(r * stride) + c * 3 + 2] = color.Red;

                }
            }


            System.IntPtr scan0 = bitmapdat.Scan0;
            Marshal.Copy(bytes, 0, scan0, stride * bitm.Height);
            bitm.UnlockBits(bitmapdat);

            return bitm;
        }

        /// <summary>
        /// Returns a tuple that contains necessary information about bitmap header 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        private Tuple<BmpHeader, DibHeader> ReadHeaders(string filename)
        {
            var bmpHeader = new BmpHeader();
            var dibHeader = new DibHeader();
            using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
            {
                using (var br = new BinaryReader(fs))
                {
                    bmpHeader.MagicNumber = br.ReadInt16();
                    bmpHeader.Filesize = br.ReadInt32();
                    bmpHeader.Reserved1 = br.ReadInt16();
                    bmpHeader.Reserved2 = br.ReadInt16();
                    bmpHeader.DataOffset = br.ReadInt32();

                    dibHeader.HeaderSize = br.ReadInt32();
                    if (dibHeader.HeaderSize != 40)
                    {
                        throw new ApplicationException("Only Windows V3 format supported.");
                    }
                    dibHeader.Width = br.ReadInt32();
                    dibHeader.Height = br.ReadInt32();
                    dibHeader.ColorPlanes = br.ReadInt16();
                    dibHeader.Bpp = br.ReadInt16();
                    dibHeader.CompressionMethod = br.ReadInt32();
                    dibHeader.ImageDataSize = br.ReadInt32();
                    dibHeader.HorizontalResolution = br.ReadInt32();
                    dibHeader.VerticalResolution = br.ReadInt32();
                    dibHeader.NumberOfColors = br.ReadInt32();
                    dibHeader.NumberImportantColors = br.ReadInt32();
                }
            }

            return Tuple.Create(bmpHeader, dibHeader);
        }
    }

    public struct MyColor
    {
        public byte Red;
        public byte Green;
        public byte Blue;
        //public byte Alpha;
    }
    public class ColorObject
    {
        public ColorObject(MyColor c)
        {
            this.Red = c.Red;
            this.Green = c.Green;
            this.Blue = c.Blue;
           // this.Alpha = c.Alpha;
        }
        public byte Red;
        public byte Green;
        public byte Blue;
       // public byte Alpha;
    }

    public class BmpHeader
    {
        public short MagicNumber { get; set; }
        public int Filesize { get; set; }
        public short Reserved1 { get; set; }
        public short Reserved2 { get; set; }
        public int DataOffset { get; set; }
    }

    public class DibHeader
    {
        public int HeaderSize { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public short ColorPlanes { get; set; }
        public short Bpp { get; set; }
        public int CompressionMethod { get; set; }
        public int ImageDataSize { get; set; }
        public int HorizontalResolution { get; set; }
        public int VerticalResolution { get; set; }
        public int NumberOfColors { get; set; }
        public int NumberImportantColors { get; set; }
        public int RowSize
        {
            get
            {
                return 4 * ((Bpp * Width) / 32);
            }
        }
    }
}

这是如何使用它:-

   Bitmap bmpImage = bmp.readPartOfImage(filePath); // path to bitmap
   pictBoxBMP.Image = bmpImage; // set the picture box image to the new Partially created bitmap 

我寻求的解决方案:- 只是正确显示部分创建的位图,我猜位图重建或使用内存映射视图读取位图时存在问题。

Update#1 : 应用@TaW solution 后,我得到了显示的颜色,即使它们与原始颜色有点不同,但可以接受。

【问题讨论】:

  • 根据您忽略调色板的图像,在每个扫描行和行顺序上填充字节...查看位图格式规范以更好地理解所有字段(维基百科有很好的起始文章 - en.wikipedia.org/wiki/BMP_file_format )
  • @AlexeiLevenkov 我已经检查过了,我读取的图像格式是 Format24bppRgb(3 个字节,一个为红色,一个为蓝色,一个为绿色)。我重建的图像是 Format24bppRgb。
  • 看起来您可能没有考虑行的 stride。大多数图像格式以某种对齐方式开始每一行,并填充前一行。
  • @CoryNelson 解释更多
  • 尽管您说“我已经检查过了”,但我还是建议您更仔细地阅读它 - 即正如@CoryNelson 建议的那样,您没有考虑“像素格式由DIB Header 或Extra 位掩码定义。像素数组中的每一行被填充为4 个字节的倍数”(来自维基)

标签: c# image bitmap memory-mapped-files


【解决方案1】:

我相信你没有正确理解 Stride。从您的颜色结构/类来看,您使用 24Bpp..

对于 24Bpp,add 的填充将是 Width % 4,因此在 RowSize getter 更改中

return 4 * ((Bpp * Width) / 32);

return (Bpp / 8 * Width) + Width % 4;

对于一般情况,您可以获得步幅的填充

Padding = Width % (4 * (4 - (Bpp / 8) ); 

根据定义,行的顺序是颠倒的(即自下而上),但您是自上而下创建的!所以在CreateBM

for (int r = 0; r < bitm.Height; r++)

for (int r = bitm.Height - 1 ; r > 0; r--)

至于颜色:先尝试修复这些东西,然后报告结果,好吗?

【讨论】:

  • 改变for循环并没有改变倒置机制,改变填充后颜色是正确的!
  • 嗯,颜色现在看起来不错,但不是“正确”的 imo!我想知道颠倒和颜色偏移是否相关.. - 更新的图像是来自您的代码还是在磁盘上?
  • 另外:请尝试使用不同的图像!每行中重复的数字很容易将一行误认为另一行!一次弄错颜色通道并选择错误的线条很可能会导致您发布的图像..
  • 您是在创建例程中还是在读取中更改了行顺序,或者两者都更改了? (同时更改两者都无济于事;-)还使用 tumasgiu 的方式来反转顺序看起来也不错;但同样:反转两次将取消更改..
  • 这是程序[更新#1]生成的图像,我首先应用了步幅部分并固定了颜色,尝试了相反的并没有反转它
【解决方案2】:

对于反转部分,您必须检查输入位图数据的顺序(自下而上或自上而下)。 您可以通过检查 DibHeader Height 属性的符号来做到这一点。 通常,负高度表示自上而下的布局。 https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376%28v=vs.85%29.aspx

然后您知道是否必须反转数据,将输入位图的第一行复制到目标位图的最后一行。

        for (int r = 0; r < bitm.Height; r++)
        {

            for (int c = 0; c < bitm.Width; c++)
            {
                ColorObject color = rowColors[r][c];
                bytes[( (bitm.Height - r - 1) * stride) + c * 3] = color.Blue;
                bytes[( (bitm.Height - r - 1) * stride) + c * 3 + 1] = color.Green;
                bytes[( (bitm.Height - r - 1) * stride) + c * 3 + 2] = color.Red;

            }
        }

【讨论】:

  • 它正确地反转了显示,另外一项修改是改变蓝色代替红色,使颜色正确显示。
  • 现在你们都帮助解决了,我应该将谁标记为正确? ——
  • 我不知道可以编辑其他人的答案。如果我知道的话,我就会完成 TaW 的回答。所以我认为正确的答案应该是他的!
  • 是的,您可以,尽管在您当前的代表级别上,编辑将经过审核过程。在 2000 年,对问题和答案的编辑将立即生效。鼓励编辑,不仅是在您想添加答案时,而且在您发现拼写和格式错误时。有一段时间(我忘了,我想是在你获得 200 次代表之前),你甚至可以为每一个获得批准的编辑获得 2 次代表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多