天天看點

數字圖像處理 使用C#進行圖像處理五 記憶體映射檔案

記憶體映射檔案

是由一個檔案到一塊記憶體的映射。Win32提供了允許應用程式把檔案映射到一個程序的函數 (CreateFileMapping)。通過記憶體映射檔案可以保留一個位址空間的區域,同時将實體存儲器送出給此區域,記憶體檔案映射的實體存儲器來自一個已經存在于磁盤上的檔案,而且在對該檔案進行操作之前必須首先對檔案進行映射。使用記憶體映射檔案處理存儲于磁盤上的檔案時,将不必再對檔案執行I/O操作,使得記憶體映射檔案在處理大資料量的檔案時能起到相當重要的作用。

C#中,System.IO.MemoryMappedFiles 這個類就提供了記憶體映射檔案的功能。文檔位址https://docs.microsoft.com/zh-cn/dotnet/api/system.io.memorymappedfiles.memorymappedfile?view=net-5.0

使用/處理流程

一般大檔案,格式和内容已知,就可以直接映射,然後進行讀取等操作;大的圖像檔案,不會像下面的例子一樣直接擷取mat或bitmap,而通常都是通過比如jpg、png的類庫逐一讀取圖像資料資訊儲存到新建立的映射檔案,然後再映射上一步儲存後的檔案,進行讀取等操作。

而在某些特殊行業,經常要面對十幾GB乃至幾十GB容量的巨型檔案,而一個32位程序所擁有的虛拟位址空間隻有232 = 4GB,顯然不能一次将檔案映像全部映射進來。對于這種情況隻能依次将大檔案的各個部分映射到程序中的一個較小的位址空間。這需要對上面的一般流程進行适當的更改:

1)映射檔案開頭的映像。

2)對該映像進行通路。

3)取消此映像

4)映射一個從檔案中的一個更深的位移開始的新映像。

5)重複步驟2,直到通路完全部的檔案資料。

數字圖像處理 使用C#進行圖像處理五 記憶體映射檔案

c++中的windows函數的處理流程,相對c#來講繁瑣很多,c#中封裝的好,但是相對速度沒有c++塊,隻是使用更簡化了

建立記憶體映射檔案

舉例說明:讀取一張圖檔,建立一個檔案,把圖檔資料寫入到建立的檔案當中,以備後面使用。

數字圖像處理 使用C#進行圖像處理五 記憶體映射檔案

原圖

Mat mat = Cv2.ImRead("C://Users//xiaomao//Desktop//122.png");
mat.CvtColor(ColorConversionCodes.BGRA2BGR);
Bitmap bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(mat);
int width = bitmap.Width;
int height = bitmap.Height;
using (MemoryMappedFile file = MemoryMappedFile.CreateFromFile("C:\\Users\\zyh\\Desktop\\122.mmf", FileMode.Create, "my_mmf", width * height * 3))
{
    stream = file.CreateViewStream();
    byte[] vs = new byte[width * height * 3];
    BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
                IntPtr iPtr = bmpData.Scan0;
    System.Runtime.InteropServices.Marshal.Copy(iPtr,vs, 0, width * height * 3);
    bitmap.UnlockBits(bmpData);
    stream.Write(vs, 0, vs.Length);
}
           

從記憶體映射的檔案中讀取資料

數字圖像處理 使用C#進行圖像處理五 記憶體映射檔案

從映射的檔案中截取的一部分圖像

MemoryMappedFile tempfile = MemoryMappedFile.CreateFromFile("C:\\Users\\xiaomao\\Desktop\\122.mmf", FileMode.Open, "T1611623016");

MemoryMappedViewAccessor handle = null;
int x = 0;
int y = 0;
int width = 1146;//圖檔寬度
int height = 50;//想要截取的高度

byte[] vs = new byte[1146 * height * 3];

for (int i = y; i < y + height; i++)
{
    //每次都重新從檔案建立不同位置的指針
    handle = tempfile.CreateViewAccessor(width * i * 3, width * 3, MemoryMappedFileAccess.Read);
    handle.ReadArray(0, vs, i * width * 3, width * 3);
}

Bitmap bitmap = new Bitmap(width, height);
BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
IntPtr iPtr = bmpData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(vs, 0, iPtr, width * height * 3);
bitmap.UnlockBits(bmpData);
this.pictureBox1.Image = bitmap;
Cv2.ImWrite("C:\\Users\\xiaomao\\Desktop\\aa.jpg", OpenCvSharp.Extensions.BitmapConverter.ToMat(bitmap));
           

相關參考

http://www.cppblog.com/woaidongmao/archive/2008/12/26/70439.html

https://blog.csdn.net/mataiyuan/article/details/78558280

繼續閱讀