Raspberry Pi OOM 杀死

问题描述 投票:0回答:1

我正在尝试创建一个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)

显然我是用暴力来解决这个问题的。但我的想法是,如果我正确地清除了东西,那么内存使用量应该保持不变,并且如果脚本可以毫无问题地显示前十几个图像,那么它应该能够继续这样做,如果我正确地清除了东西,对吗?我唯一能想到的就是关闭我打开的图像来修改并终止旧进程。我肯定想念这里的一些东西。有人能看到吗?

python raspberry-pi feh
1个回答
0
投票

我认为,这应该有帮助:

  1. 获取尺寸后应关闭图像,以防止同时将所有图像加载到 RAM 中。
  2. 考虑使用上下文管理器自动关闭它们(
    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)
© www.soinside.com 2019 - 2024. All rights reserved.