Windowsの再インストールというトラブルに合い、現在Pythonはラズベリーパイだけでやっています。ただコードはWindows10のメモ帳で書いています。WinSCPでラズベリーパイへファイル転送し、VNCviewerでラズベリーパイ上のThonnyを起動しています(ラズベリーパイは小モニターを付けて、棚の上に置いています)。WinSCP・VNCviewerは、IPアドレス入力だけでつながります。暗号化・認証でもエラーは出たことがありません。ラズベリーパイOSのBuster・Bookwormと大変相性がいいです。ラズベリーパイのPythonコードを作成する上で、'三種の神器'です。
開発環境の話が少し長くなりましたが、今回はラズベリーパイ4B上で、OSのbookwormとbusterを使いました。タイトルにあるPythonのMultiprocessingのManegerのメモリー共有は、「① 前設定が簡単である」、「② listが使える」という利点があります。ただManagerは「処理速度が少し遅い」という欠点があります。またmultiprocessingのメモリー共有には、Manager以外にctypesも含め多くの高速な方法があります。しかし今回は、Managerが「① 前設定が簡単」であることに着目しました。というより初心者なので、他の方法は少し難解でした。そしてndarrayを共有する上で、「② listが使える」ことも魅力でした。
コードは、最初にcore1で、4個の画像ファイルを読み込みndarrayとします。さらに同じcore1で、画像ndarrayを「Managerのlist」に書き込みます。次にcore4で、この「Managerのlist」を読み出し、画像ndarrayに戻します。最後に同じcore4で、画像ndarrayをディスプレイに表示します。これのループです。
またコード作成の途上で意外だったのは、「Managerのlist」の「読み出しエラー」が多いことです。ファンを回して冷却すると、エラー率は少し下がります。しかしファンで冷却しても、「読み出しエラー」はまだ残ります。今まで、「pythonの標準のlist」操作でエラーは無かったので意外でした。ほとんどの部分が、「Managerのlist」の「読み出しエラー」のクリアです。以下がコードです。
# manager-ndarray---bookworm-buster.py
import time
from multiprocessing import Manager, Value, Process
import numpy as np
import cv2 as cv
# Raspberry Pi 4B, 8G under cooling on Thonny
# ------------------------------------------------
# Buster(x32) OS: Linux raspberrypi 5.10.103-v7l+ #1529 SMP Tue Mar 8 12:24:00 GMT 2022 armv7l GNU/Linux
# Python 3.7.3 (default, Oct 31 2022, 14:04:00)
# numpy 1.20.1
# OpenCV 3.4.15.55
# ------------------------------------------------
# Bookworm(x64) OS: Linux raspberrypi 6.1.0-rpi7-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.63-1+rpt1 (2023-11-24) aarch64 GNU/Linux
# Python 3.11.2 (main, Mar 13 2023, 12:18:29)
# numpy 1.24.2
# OpenCV 4.6.0 (sudo apt install python3-opencv)
# ------------------------------------------------
def process1(vcount00, f_plist0, plist0):
img00 = cv.imread("d00.png") #640w x 480h
img01 = cv.imread("d01.png")
img02 = cv.imread("d02.png")
img03 = cv.imread("d03.png") #warning for bookworm
height00 = 480
width00 = 640
rgb00 = 3
try:
while(True):
count00_old = vcount00.value
q, mod = divmod(count00_old, 4)
if mod == 0:
img480 = img00.copy()
elif mod == 1:
img480 = img01.copy()
elif mod == 2:
img480 = img02.copy()
elif mod == 3:
img480 = img03.copy()
## preWriting 3D-->4D
#print(repr(img480))
img480_4d = np.reshape(img480, [1, height00, width00, rgb00])
#print(repr(img480_4d))
## Writing to plist0
while True:
time.sleep(0.0001)
if f_plist0.value == 0:
f_plist0.value = 9
if f_plist0.value == 9 or f_plist0.value == 1:
f_plist0.value = 1
break
plist0[:]= []
for i in img480_4d:
plist0.append(i)
if len(plist0)==0:
print("plist0==0") #### No error
f_plist0.value = 9
vcount00.value += 1
time.sleep(0.5)
except KeyboardInterrupt:
print('process1 excepted')
def process4(vcount00, f_plist0, plist0):
img = cv.imread("Init.png") #warning for bookworm
cv.imshow("Image1", img)
key = cv.waitKey(1000)
height00 = 480
width00 = 640
rgb00 = 3
# a_ndarray00c = np.reshape(img, [1, height00, width00, rgb00])
count0_old=0
try:
while(True):
l_ndarray00=[]
time.sleep(0.001)
if count0_old < vcount00.value :
count0_old = vcount00.value
## Reading from plist0
i_retry = 0
while True:
while True:
time.sleep(0.0001)
if f_plist0.value == 9:
f_plist0.value = 4
break
for i in plist0:
l_ndarray00.append(i)
f_plist0.value = 9
# double 'while' loop for read-error
i_retry += 1
if i_retry >= 20:
print("i_retry >= 20 error")
break
if len(l_ndarray00) == 0:
time.sleep(0.0001)
continue
else:
print(i_retry) #### sometimes out of 1
a_ndarray00 = np.array(l_ndarray00, dtype="uint8")
a_ndarray00c = a_ndarray00.copy()
break
# 4D-->3D
a_ndarray00c333 = np.reshape(a_ndarray00c, [height00, width00, rgb00])
img480 = cv.resize(a_ndarray00c333, (width00, height00))
## imshow
cv.imshow("Image1", img480)
key = cv.waitKey(10)
except KeyboardInterrupt:
cv.destroyAllWindows()
print('process4 excepted')
if __name__ == "__main__":
with Manager() as manager:
vcount00 = manager.Value('i', 0)
vcount00.value = 0
f_plist0 = manager.Value('i', 0)
f_plist0.value = 1
plist0 = manager.list()
process1 = Process(target=process1, args=[vcount00, f_plist0, plist0])
process4 = Process(target=process4, args=[vcount00, f_plist0, plist0])
process1.start()
process4.start()
process1.join()
process4.join()
動かしてみて、bookworm(x64)とBuster(x32)は、ほぼ同じ動作でした。ただ画面操作などをすると、時々1以外が出ます。割り込みが、同じcoreを使っているのかもしれません。動作上は、今回のシンプルなエラー回避策により、「Managerのlist」で、共有できることがわかりました。
お忙しい中、ご覧いただきありがとうございました。
なお公開プログラムは、コピー・改変も自由です。著作権に関する問題も発生しません。
本ページは、Pythonのmultiprocessing 等を参考にしています。
https://docs.python.org/ja/3.7/library/multiprocessing.html
https://numpy.org/doc/1.16/reference/generated/numpy.array.html
https://numpy.org/doc/1.16/reference/generated/numpy.reshape.html
以下はプログラム中で使用した画像です。
Init.png
d00.png
d01.png
d02.png
d03.png