Python 3.x 在运行Jessie和Update的Raspberry Pi 2 Model B上使用python 3显示图像流时,缓存溢出
我对python非常陌生。我正在编写一段“概念验证”代码;用PiCamera在运行Jessie的覆盆子Pi上 我的代码基于以下教程代码: 点击按钮显示图像后,代码将启动PiCamera并开始获取连续捕获,将其传递到流,并对其应用十字光标 它工作得很好…但在两分钟多一点后,磁盘驱动器亮起,开始急剧减速。一旦程序中断,一切都会好起来。我看了一些日志,但我一辈子都找不到什么缓存溢出或者为什么。我尝试了许多不同的方法,并试图将它们作为评论保留下来。我怀疑这与必须清除tkinter中的图像有关,但即使这样也似乎不起作用,使视频闪烁不均匀 任何帮助都会很好!我已经开始使用opencv进行探索。还在安装 谢谢 守则:Python 3.x 在运行Jessie和Update的Raspberry Pi 2 Model B上使用python 3显示图像流时,缓存溢出,python-3.x,tkinter,raspberry-pi2,Python 3.x,Tkinter,Raspberry Pi2,我对python非常陌生。我正在编写一段“概念验证”代码;用PiCamera在运行Jessie的覆盆子Pi上 我的代码基于以下教程代码: 点击按钮显示图像后,代码将启动PiCamera并开始获取连续捕获,将其传递到流,并对其应用十字光标 它工作得很好…但在两分钟多一点后,磁盘驱动器亮起,开始急剧减速。一旦程序中断,一切都会好起来。我看了一些日志,但我一辈子都找不到什么缓存溢出或者为什么。我尝试了许多不同的方法,并试图将它们作为评论保留下来。我怀疑这与必须清除tkinter中的图像有关,但即使这样
# Simple enough, just import everything from tkinter.
from tkinter import *
import picamera
import picamera.array
import time
import threading
import io
import numpy as np
from PIL import Image, ImageTk
# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
# Create an array representing a 1280x720 image of
# a cross through the center of the display. The shape of
# the array must be of the form (height, width, color)
# Define settings upon initialization. Here you can specify
def __init__(self, master=None):
# parameters that you want to send through the Frame class.
Frame.__init__(self, master)
#reference to the master widget, which is the tk window
self.master = master
#with that, we want to then run init_window, which doesn't yet exist
self.init_window()
#Creation of init_window
def init_window(self):
# changing the title of our master widget
self.master.title("GUI")
# allowing the widget to take the full space of the root window
self.pack(fill=BOTH, expand=1)
# creating a menu instance
menu = Menu(self.master)
self.master.config(menu=menu)
# create the file object)
file = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
file.add_command(label="Exit", command=self.client_exit)
#added "file" to our menu
menu.add_cascade(label="File", menu=file)
# create the file object)
edit = Menu(menu)
# adds a command to the menu option, calling it exit, and the
# command it runs on event is client_exit
edit.add_command(label="Show Img", command=self.showImg)
edit.add_command(label="Show Text", command=self.showText)
#added "file" to our menu
menu.add_cascade(label="Edit", menu=edit)
self.trim_running_bool = False
def showImg(self):
self.trim_running_bool = True
trim_thrd_thread = threading.Thread(target=self._cam_thread_def)
trim_thrd_thread.start()
self.update_idletasks()
def _cam_thread_def(self):
img_stream = io.BytesIO()
frame_count = 0
with picamera.PiCamera() as camera:
camera.resolution = (400, 300)
## while True: ### tried it this way too
for xxx in range(0,900):
img_stream = io.BytesIO()
frame_count = frame_count + 1
print(frame_count," ", xxx)
if self.trim_running_bool == False:
print("break")
break
camera.capture(img_stream, 'jpeg', use_video_port=True)
img_stream.seek(0)
img_load = Image.open(img_stream)
for xl_line in range(0,196,4):
img_load.putpixel((xl_line, 149), (xl_line, 0, 0))
xll=xl_line+2
img_load.putpixel((xl_line, 150), (xl_line, xl_line, xl_line))
img_load.putpixel((xl_line, 151), (xl_line, 0, 0))
(xl_line)
for xr_line in range(208,400,4):
clr = 400 - xr_line
img_load.putpixel((xr_line, 149), (clr, 0, 0))
img_load.putpixel((xr_line, 150), (clr, clr, clr))
img_load.putpixel((xr_line, 151), (clr, 0, 0))
(xr_line)
for yt_line in range(0,146,4):
clrt = int(yt_line * 1.7)
img_load.putpixel((199, yt_line), (clrt, 0, 0))
img_load.putpixel((200, yt_line), (clrt, clrt, clrt))
img_load.putpixel((201, yt_line), (clrt, 0, 0))
(yt_line)
for yb_line in range(158,300,4):
clrb = int((300 - yb_line) * 1.7)
img_load.putpixel((199, yb_line), (clrb, 0, 0))
img_load.putpixel((200, yb_line), (clrb, clrb, clrb))
img_load.putpixel((201, yb_line), (clrb, 0, 0))
(yb_line)
img_render = ImageTk.PhotoImage(img_load)
# labels can be text or images
img = Label(self, image=img_render)
img.image = img_render
img.place(x=0, y=0)
self.update_idletasks()
img_stream.seek(0)
img_stream.truncate(0)
# tried these:
## img_stream.flush()
## print("flushed ", img_stream)
## print("2nd ",img_stream)
## del img_load
##
##
## rawCapture.truncate(0)
##
## rawCapture.seek(0)
## rawCapture.truncate(0)
## del render
## img.image = None
## foregnd_image = None
(xxx)
pass
def showText(self):
text = Label(self, text="Hey there good lookin!")
text.pack()
def client_exit(self):
self.trim_running_bool = False
exit()
# root window created. Here, that would be the only window, but
# you can later have windows within windows.
root = Tk()
root.geometry("400x300")
#creation of an instance
app = Window(root)
#mainloop
root.mainloop()
每次通过循环创建一个新图像对象和一个新标签,以及一些其他对象。这是内存泄漏,因为您从未破坏旧图像或旧标签 一般来说,您应该只创建一个标签,然后在循环中每次都使用
the_label.configure(image=the_image)
。这样,您就不需要创建新标签或调用place
更好的是,由于标签会在相关图像更改时自动更新,因此只需更改图像对象本身中的位,标签就会自动更新
最简单的解决方案是将图像创建移动到一个函数,以便您正在创建的所有对象都是本地对象,当函数返回时,这些对象可以被自动垃圾收集
第一步是在主线程中创建单个标签和单个图像:
class Window(Frame):
def __init__(self, master=None):
...
self.image = PhotoImage(width=400, height=300)
self.label = Label(self, image=self.image)
...
接下来,创建一个函数,将新数据复制到图像中。不幸的是,tkinter实现的copy
方法不支持底层图像对象的全部功能。此处介绍了一种变通方法:
注意:变通方法示例是一个通用变通方法,它使用的参数比我们需要的多。在下面的示例中,我们可以省略许多参数。下面是底层tk photo对象copy
方法的文档:
实现看起来像这样(我猜,我没有一个好的方法来测试它):
最后,更新图像的循环会简单得多:
while True:
self.new_image()
# presumeably there's some sort of sleep here so you're
# not updating the image faster than the camera can
# capture it.
我不知道新图像的运行速度有多快。如果它能在200毫秒或更短的时间内运行,你甚至不需要线程。相反,您可以使用after
定期运行该函数
注意:我已经很长时间没有使用Tkinter照片图像了,我也没有好的方法来测试这一点。将此作为指导,而不是最终解决方案 我无法运行它并测试它,但可能您遇到了问题,因为在
for
循环中,您多次创建ByteIO
、Image
、ImageTk
和Label
而不是使用ByteIO
、Image
、ImageTk
和Label
的单个实例。感谢您的关注。我想我在那个地方试过了,有没有试过。我一直在尝试一些东西,所以我不确定我在这一点上尝试了什么。我也试过用“while True”这个词,还是一样的问题。谢谢!我很快就会测试这个。。我开始怀疑情况是否如此。谢谢你花时间。就像通常看起来的那样,解决方案优雅而简单,但不知何故愚蠢地难以捉摸。您让我走上了正确的轨道,这表明我在重复创建标签对象。我所要做的就是在“while True”之前创建标签,然后对名称进行一些按摩,它运行了45分钟以上,没有任何问题。谢谢你的帮助。
while True:
self.new_image()
# presumeably there's some sort of sleep here so you're
# not updating the image faster than the camera can
# capture it.