我正在尝试创建一个Python脚本,它的作用就像一个数码相框,在Raspberry Pi Zero上运行,连接到显示器。我设法让脚本满足我的要求。但是,我遇到了内存管理问题,因为脚本进程不断因 OOM 而被终止。
Dec 14 15:55:25 raspberrypi kernel: [ 2987.175293] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),global_oom,task_memcg=/,task=python,pid=1759,uid=1000
Dec 14 15:55:25 raspberrypi kernel: [ 2987.175357] Out of memory: Killed process 1759 (python) total-vm:273328kB, anon-rss:250680kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:276kB oom_score_adj:0
我之前几乎没有使用过Python,所以我不太了解Python如何管理内存。以下是我的脚本:
from PIL import Image
import os
import random
import subprocess
import time
import signal
def scan_images(image_dir):
print("scanning images")
image_files = []
for root, dirs, files in os.walk(image_dir):
for file in files:
if file.lower().endswith(('.png', '.jpg', '.jpeg')):
image_files.append(os.path.join(root, file))
print("finished scanning")
return image_files
def create_slideshow(image_dir):
# Register a signal handler for interrupt (Ctrl+C)
signal.signal(signal.SIGINT, signal_handler)
try:
os.environ['DISPLAY'] = ':0.0'
image_files = scan_images(image_dir)
vertical_images = [img for img in image_files if Image.open(os.path.join(image_dir, img)).height > Image.open(os.path.join(image_dir, img)).width]
horizontal_images = [img for img in image_files if Image.open(os.path.join(image_dir, img)).width >= Image.open(os.path.join(image_dir, img)).height]
random.shuffle(vertical_images)
print("found {} vertical images".format(len(vertical_images)))
random.shuffle(horizontal_images)
print("found {} horizontal images".format(len(horizontal_images)))
feh_current_process = None
feh_previous_process = None
while True:
# Display horizontal images on their own
for horizontal_image in horizontal_images:
# Launch new feh process
print("executing command")
feh_previous_process = feh_current_process
feh_current_process = subprocess.Popen(['feh', '--fullscreen', '--auto-zoom', '--hide-pointer', os.path.join(image_dir, horizontal_image)])
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# terminate old feh
print("terminate old feh")
if feh_previous_process != None:
feh_previous_process.terminate()
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# Display paired vertical images with an additional vertical image on the right side
for i in range(0, len(vertical_images)):
# Select vertical image
current_vertical_image = vertical_images[i]
# Select one additional random vertical image for the right side
additional_vertical_image = random.choice(vertical_images)
# Create composite image with two vertical images side by side
composite_image_path = os.path.join(image_dir, 'composite_image.jpg')
create_composite_image(image_dir, current_vertical_image, additional_vertical_image, composite_image_path)
# Display the composite image in the background
display_command = ['feh', '--fullscreen', '--auto-zoom', '--hide-pointer', composite_image_path]
print("executing command {}".format(display_command))
feh_previous_process = feh_current_process
feh_current_process = subprocess.Popen(display_command)
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# terminate old feh
print("terminate old feh")
if feh_previous_process != None:
feh_previous_process.terminate()
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
except KeyboardInterrupt:
print("Slideshow interrupted. Cleaning up...")
cleanup()
def signal_handler(sig, frame):
# Handle interrupt signal (Ctrl+C)
raise KeyboardInterrupt
def cleanup():
# Additional cleanup steps if needed
sys.exit(0)
def create_composite_image(image_dir, image1, image2, output_path):
img1 = Image.open(os.path.join(image_dir, image1))
img2 = Image.open(os.path.join(image_dir, image2))
# Resize both vertical images to the height of the taller one
max_height = max(img1.height, img2.height)
img1 = img1.resize((int(img1.width * max_height / img1.height), max_height))
img2 = img2.resize((int(img2.width * max_height / img2.height), max_height))
# Create a new image with double width for vertical images
composite_img = Image.new('RGB', (img1.width + img2.width, max_height))
composite_img.paste(img1, (0, 0))
composite_img.paste(img2, (img1.width, 0))
# Save the composite image
composite_img.save(output_path)
img1.close()
img2.close()
composite_img.close()
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("Usage: python slideshow.py /path/to/image/directory")
sys.exit(1)
image_directory = sys.argv[1]
if not os.path.isdir(image_directory):
print(f"Error: {image_directory} is not a valid directory.")
sys.exit(1)
create_slideshow(image_directory)
显然我是用暴力来解决这个问题的。但我的想法是,如果我正确地清除了东西,那么内存使用量应该保持不变,并且如果脚本可以毫无问题地显示前十几个图像,那么它应该能够继续这样做,如果我正确地清除了东西,对吗?我唯一能想到的就是关闭我打开的图像来修改并终止旧进程。我肯定想念这里的一些东西。有人能看到吗?
我认为,这应该有帮助:
with Image.open(...) as image: ...
)from PIL import Image
import os
import random
import subprocess
import time
import signal
def scan_images(image_dir):
print("scanning images")
image_files = []
for root, dirs, files in os.walk(image_dir):
for file in files:
if file.lower().endswith(('.png', '.jpg', '.jpeg')):
image_files.append(os.path.join(root, file))
print("finished scanning")
return image_files
def read_image_resolution(image_file_path):
with Image.open(image_file_path) as img:
return (img.width, img.height)
def create_slideshow(image_dir):
# Register a signal handler for interrupt (Ctrl+C)
signal.signal(signal.SIGINT, signal_handler)
try:
os.environ['DISPLAY'] = ':0.0'
image_files = scan_images(image_dir)
vertical_images = []
horizontal_images = []
for image in image_files:
image_file_path = os.path.join(image_dir, image)
w, h = read_image_resolution(image_file_path)
if w > h:
horizontal_images.append(image_file_path)
else:
vertical_images.append(image_file_path)
random.shuffle(vertical_images)
print("found {} vertical images".format(len(vertical_images)))
random.shuffle(horizontal_images)
print("found {} horizontal images".format(len(horizontal_images)))
feh_current_process = None
feh_previous_process = None
while True:
# Display horizontal images on their own
for horizontal_image in horizontal_images:
# Launch new feh process
print("executing command")
feh_previous_process = feh_current_process
feh_current_process = subprocess.Popen(['feh', '--fullscreen', '--auto-zoom', '--hide-pointer', horizontal_image])
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# terminate old feh
print("terminate old feh")
if feh_previous_process != None:
feh_previous_process.terminate()
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# Display paired vertical images with an additional vertical image on the right side
for i in range(0, len(vertical_images)):
# Select vertical image
current_vertical_image = vertical_images[i]
# Select one additional random vertical image for the right side
additional_vertical_image = random.choice(vertical_images)
# Create composite image with two vertical images side by side
composite_image_path = os.path.join(image_dir, 'composite_image.jpg')
create_composite_image(current_vertical_image, additional_vertical_image, composite_image_path)
# Display the composite image in the background
display_command = ['feh', '--fullscreen', '--auto-zoom', '--hide-pointer', composite_image_path]
print("executing command {}".format(display_command))
feh_previous_process = feh_current_process
feh_current_process = subprocess.Popen(display_command)
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
# terminate old feh
print("terminate old feh")
if feh_previous_process != None:
feh_previous_process.terminate()
# Pause for a few seconds (adjust as needed)
print("sleeping 5 sec")
time.sleep(5)
except KeyboardInterrupt:
print("Slideshow interrupted. Cleaning up...")
cleanup()
def signal_handler(sig, frame):
# Handle interrupt signal (Ctrl+C)
raise KeyboardInterrupt
def cleanup():
# Additional cleanup steps if needed
sys.exit(0)
def create_composite_image(image1, image2, output_path):
with Image.open(image1) as img1, \
Image.open(image2) as img2:
# Resize both vertical images to the height of the taller one
max_height = max(img1.height, img2.height)
img1 = img1.resize((int(img1.width * max_height / img1.height), max_height))
img2 = img2.resize((int(img2.width * max_height / img2.height), max_height))
# Create a new image with double width for vertical images
with Image.new('RGB', (img1.width + img2.width, max_height)) as composite_img:
composite_img.paste(img1, (0, 0))
composite_img.paste(img2, (img1.width, 0))
# Save the composite image
composite_img.save(output_path)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("Usage: python slideshow.py /path/to/image/directory")
sys.exit(1)
image_directory = sys.argv[1]
if not os.path.isdir(image_directory):
print(f"Error: {image_directory} is not a valid directory.")
sys.exit(1)
create_slideshow(image_directory)