使用内存映射视图查看大型位图图像

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

这里的主要问题是我无法将其直接加载到内存中,因此我尝试使用内存映射视图库来加载它, 参考参考 文献1 , 参考文献2 , 参考文献3 , 参考文献4和参考文献五 。

到目前为止我所得到的是: –根据需要读取部分位图(例如我读取前200行)。 从我读取的行创建另一个位图并显示它。

Porblem: –重建的位图图像部分丢失颜色信息并向上显示。

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

真实的形象: – 在此处输入图像描述

输出应该是(选择前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 { ///  /// It opens the image using memory mapped view and read the needed /// parts, then call CreateBM to create a partially bitmap ///  /// Path to the physical bitmap ///  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 rowColors = new Dictionary(); 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 ); } ///  /// Used to create a bitmap from provieded bytes ///  /// Contains bytes of bitmap ///  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; } ///  /// Returns a tuple that contains necessary information about bitmap header ///  ///  ///  private Tuple 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 

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

更新#1 :应用@TaW解决方案后,我显示了颜色,即使它们与原始颜色略有不同,但它已被接受。 在此处输入图像描述

我相信你没有得到Stride的权利。 从你的Color结构/类来看,你使用24Bpp ..

对于24Bpp,要添加的填充将是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--) 

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

对于反转部分,您必须检查输入位图数据的顺序(自下而上或自上而下)。 您可以通过检查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; } }