How To Use Memory Mapped File in Python Linux

Viewed 1466

As I saw in the Python documentation,

https://docs.python.org/3/library/mmap.html.

Python in Linux can fully support the memory-mapped file. However, while I am trying to apply this idea to my application. I cannot run the sample.

My application is that sending the frames from Python file (client) to the another Python file (server).

Client Code

import mmap
import time
import os
import cv2 as cv

print("Opening camera...")
cap = cv.VideoCapture('/home/hunglv/Downloads/IMG_8442.MOV')

mm = None
try:
    while True:
        ret, img = cap.read()
        if not ret:
            break
        if mm is None:
            mm = mmap.mmap(-1,img.size,mmap.MAP_SHARED, mmap.PROT_WRITE)

        # write image
        start = time.time()
        buf = img.tobytes()
        mm.seek(0)
        mm.write(buf)
        mm.flush()  
        stop = time.time()
        print("Writing Duration:", (stop - start) * 1000, "ms")
except KeyboardInterrupt:
    pass
print("Closing resources")
cap.release()
mm.close()

Server Code

import mmap
import time
import os
import cv2 as cv
import numpy as np

shape = (1080, 1920, 3)
n = np.prod(shape)
mm = mmap.mmap(-1, n)

while True:
    # read image
    print (mm)
    start = time.perf_counter()
    mm.seek(0)
    buf = mm.read(12)
    img = np.frombuffer(buf, dtype=np.uint8).reshape(shape)
    stop = time.perf_counter()

    print("Reading Duration:", (stop - start) * 1000, "ms")
    cv.imshow("img", img)
    key = cv.waitKey(1) & 0xFF
    key = chr(key)
    if key.lower() == "q":
        break
cv.destroyAllWindows()
mm.close()

On the server-side, I set the memory index at 0, and try to read the bytes from memory. However, it seems to be that the server cannot read correctly the data from client.

[Updated] I've tried to read out the first 12 bytes at the server-side. The value is constant, not changing anymore.

b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

Besides, The first 12 bytes of a random frame is

b'\xf5\xff\xff\xf0\xfa\xfe\xdf\xe9\xed\xd2\xdc\xe0'

1 Answers

First I found example which probably works but it uses tagName (the same for client and server) and it means it is only for Window:

python-mmap-ipc


Next I found code which works on Linux:

Sharing Python data between processes using mmap.

It creates real file on disk, resizes it to size of image and then it uses its fd in mmap()


I use web camera for test.

Server

import mmap
import time
import os
import cv2

print("Opening camera...")

cap = cv2.VideoCapture(0)
#print(cap.get(cv.CAP_PROP_FRAME_WIDTH))  # 640
#print(cap.get(cv.CAP_PROP_FRAME_HEIGHT)) # 480

shape = (480, 640, 3)
n = (480*640*3)

fd = os.open('/tmp/mmaptest', os.O_CREAT | os.O_TRUNC | os.O_RDWR)
#os.write(fd, b'\x00' * n)  # resize file
os.truncate(fd, n)  # resize file

mm = None
try:
    while True:
        ret, img = cap.read()
        
        if not ret:
            break
        
        if mm is None:
            mm = mmap.mmap(fd, n, mmap.MAP_SHARED, mmap.PROT_WRITE)  # it has to be only for writing

        # write image
        start = time.perf_counter()
        
        buf = img.tobytes()
        mm.seek(0)
        mm.write(buf)
        mm.flush()
        
        stop = time.perf_counter()

        print("Writing Duration:", (stop - start) * 1000, "ms")
except KeyboardInterrupt:
    pass

print("Closing resources")
cap.release()
mm.close()

Client

import mmap
import time
import os
import cv2
import numpy as np

shape = (480, 640, 3)
n = (480*640*3)

fd = os.open('/tmp/mmaptest', os.O_RDONLY)

mm = mmap.mmap(fd, n, mmap.MAP_SHARED, mmap.PROT_READ)  # it has to be only for reading

while True:
    # read image
    start = time.perf_counter()
    
    mm.seek(0)
    buf = mm.read(n)
    img = np.frombuffer(buf, dtype=np.uint8).reshape(shape)
    
    stop = time.perf_counter()

    print("Reading Duration:", (stop - start) * 1000, "ms")

    cv2.imshow("img", img)
    key = cv2.waitKey(1) & 0xFF
    key = chr(key)
    if key.lower() == "q":
        break
    
cv2.destroyAllWindows()
mm.close()

BTW: probably mmap() with -1 (without creating file on disk) could work with threads (or forks) because they share the same memory.

Related