簡體   English   中英

從單個文件讀取和寫入多個壓縮幀

[英]Reading and Writing multiple compressed frames from single file

我有非常大的字節數據存儲在 txt 文件(每個大小約為 1TB,(4 個深度相機同時運行 24-7 個)以大約每分鍾 4GB 的速度寫入硬盤驅動器)。

每個文件都包含大量以字節編碼的圖像幀。 每幀都使用 lz4 壓縮進行壓縮,並寫入相應的攝像機文件名,雖然只有 4 個文件,但對於更長的記錄會增加。

當只有數據時,每個幀的大小都是未知的。

每個幀由一個未壓縮的字節數組模式分隔,該模式連接到已壓縮幀的末尾。 像這樣:

                map_to_sort.sort()
                frame_ender_bytes = bytearray("<FRAME_END>", 'utf-8')
                for k, frame_compressed in map_to_sort.mapper.items():
                    with lz4.frm.open(frame_compressed[0], 'ab') as fp:
                        fp.write(frame_compressed[1] + frame_ender_bytes)

我正在嘗試讀取每一幀,解壓縮它,並將未壓縮的幀寫入另一個 12TB 硬盤驅動器(在 18 小時內,或者最好更少,(比將數據寫入和壓縮到文件所需的時間更少)。我是不幸的是,這個項目無法使用大容量 SSD,所以我必須了解如何管理讀取和寫入壓縮數據到硬盤的瓶頸。

關於如何解決它,我提出了幾種不同的想法,但是它們似乎都沒有那么好。 當然,我嘗試過一些幼稚的方法來逐字節讀取文件(太長而無法讀取)。 我也試過:

以大約每幀大小的塊讀取文件並使用拆分 function 進行檢查,如下所示:

import lz4.frame as frm
import numpy as np
def chunky(stream):
    buffer = bytearray()
    while True: 
        chunk = stream.read(40096)
        if chunk:
            buffer += chunk
        else:
            break
        while True:
            combine = buffer.split(b'<FRAME_END>', 1)
            if len(combine) < 2:
                break
            else:
                buffer = combine[1]
                part = combine[0]
                yield part

#................................................
# Elsewhere in code
# dec_path is a string variable that stores the decompressed file path corresponding to the camera path file
with frm.open(camera_file_path, 'rb') as fp:
    for ch in chunky(fp):
        output_arr = ch
        decompressed_frame = frm.decompress(output_arr)
        with frm.open(dec_path, 'ab') as fj:
            fj.write(decompressed_frame)

這種方法當然有很多邊緣情況((如果 <FRAME_END> 被讀取分割成一個塊讀取怎么辦......,分割 function 將不起作用,它會顯示一個無法識別的幀.(即使這樣也不會經常發生,解壓和寫入的時間仍然大於18小時)

創建一個 JSON/txt 文件,其中包含類似 map 的結構。 每個鍵映射到每個幀的開始和結束位置(以字節為單位)。 然后將數據存儲為字典,然后利用它以適當的字節數讀取每一幀。 編寫壓縮文件:

# In this example there are no separators between each frame as I am keeping track of positions.
# map_to_sort is a dictionary organized like so 
# int k : ["E:\\Frame_Data\cam_compressed_0.txt", #compressed frame, 56000, 10000, "E:\\Frame_Data\\cam_compressed_0_mapper_data.txt"]
map_to_sort.sort()
for k, frame_compressed in map_to_sort.mapper.items():
    with frm.open(frame_compressed[0], 'ab') as fp:
         fp.write(frame_compressed[1])
    with open(frame_compressed[4], 'w') as fil:
         str_to_write = str(k) + "," + str(frame_compressed[2]) + "," + str(frame_compressed[3]) + "," + frame_compressed[0] + "," + "\n"
         fil.write(str_to_write)
map_to_sort.clear()

讀寫解壓文件

with open(TEXT_COMPRESSED_MAPPER_PATH, 'r') as fr:
     for line in fr:
         str_arr = (line.strip()).split(",")
         file_comp_read_from = str_arr[3]
         start_byte_pos = str_arr[1]
         end_byte_pos = str_arr[2]
         with frm.open(str_arr[3], 'rb') as fp:
             fp.seek(start_byte_pos, 0)
             fp.read(end_byte_pos - start_byte_pos)
         # Same decompression code as before

然而,這樣做的缺點是文本或 json 文件(並非無關緊要)和 RAM(RAM 限制)占用的 memory。 以及幀讀取不一致的事實,我會得到許多沒有正確解壓縮的幀。 也許我不確定我是否正在收集相對於框架開始和結束位置的文件大小的真實確切位置。 如果有人有任何指示也可以提供幫助。

最有可能幫助的最后一件事是將 lz4 壓縮級別從 7 提高到更高的級別。 較小的文件大小無疑會減少管理。 (解壓縮在我的 CPU 上不是那么密集)。

在壓縮級別 7 時,我已經以大約 70% 的速度使用了所有 8 個內核,在級別 8 壓縮時,我開始丟失幀,因為解壓縮時間太長,並且 CPU 使用率達到 98%。

很多壓縮代碼使用多線程、多處理的組合。 代碼太長,不能在這里發布,我也不一定允許發布包含它的鏈接(目前還不是開源的,如果你願意,我可以發布其中的一部分)。

基本思想是讓一個進程處理記錄並將幀放入共享隊列中,另一個進程同時發生,檢查隊列是否為空,然后利用線程一次壓縮多個幀來處理隊列(幀的彈出) . 然后對未排序的幀進行排序並按順序寫入相應的文件。 如果有人對如何顯着提高壓縮率而不會對我的 CPU 造成太大壓力有任何指示,那就太好了。

我對這種設計不太滿意,因為在兩個進程之間使用共享隊列並不是很好的做法。 但是,我認為我沒有什么選擇,因為我使用的英特爾深度攝像頭與多處理模塊配合得不是特別好,所以我無法輕松地為每個攝像頭設置一個進程。 英特爾深度攝像頭 python 模塊的實施也不適合以簡單的方式同時記錄和 stream。

如果您希望在我處理文件壓縮和 i/o 的方式上查看更多代碼,請告訴我。

隨意提及有損或其他更快或

抱歉信息量很大。

TLDR:在 python 中使用 lz4 壓縮在短時間內解壓大量文件,然后利用 I/0 將解壓后的文件寫入另一個硬盤驅動器時遇到問題。 上面的一些代碼和確切信息。 目標是能夠在 18 小時或更短的時間內解壓縮所有文件並將它們寫入另一個硬盤驅動器。

編輯

我能夠使用解決方案中建議的兩種方法獲得我正在尋找的壓縮級別。 (更快的讀寫時間)。 然而,這些在 6 級壓縮而不是 lz4 下使用 gzip 效果最好。 我看到我的大多數文件的壓縮比約為 6:1。 您的 cpu 的核心越多,質量越高,您將獲得更好的結果。 我有 4 個同步深度流,使用 AMD Ryzen 7 5800X 處理器,任何更高的壓縮級別,我開始丟幀。 理論上,如果你有更多的核心,你可能會得到更好的結果。 我還沒有測試過這個。 您將遇到的主要瓶頸將是硬盤的速度。 如果您有預算,我強烈建議您使用 SSD。

這是使用固定長度 header 拆分文件中的 blob 的概念證明。 不確定這是否可以解決您所有的問題,但可能是部分問題。

import random
import string
import tempfile
import os

def bytes_generator():
    out = []
    for i in range(random.randint(10, 20)):
        out.append(random.choice(string.ascii_lowercase))
    return ''.join(out).encode('utf-8')

INT_BYTE_SIZE = 8
def int_to_bytes(x: int) -> bytes:
    return x.to_bytes(INT_BYTE_SIZE, 'big')
def int_from_bytes(xbytes: bytes) -> int:
    return int.from_bytes(xbytes, 'big')

def run():
    filehandle = tempfile.TemporaryFile()
    blobs = []
    for i in range(10):
        data = bytes_generator()
        blobs.append(data)
        int_bytes = int_to_bytes(len(data))
        filehandle.write(int_bytes)
        filehandle.write(data)

    # Reset as if just opened
    filehandle.seek(0, os.SEEK_SET)

    # Get file legnth
    filehandle.seek(0, os.SEEK_END)
    file_length = filehandle.tell()

    # Reset for reading
    filehandle.seek(0, os.SEEK_SET)

    i = 0
    while filehandle.tell() < file_length:
        data_length = int_from_bytes(filehandle.read(INT_BYTE_SIZE))
        read_data = filehandle.read(data_length)
        assert read_data == blobs[i]
        i += 1

if __name__ == "__main__":
    run()

Converting int to bytes in Python 3或多或少中竊取 int/byte 轉換

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM